[Support] Remove virtual functions from formatv (#207516)
Currently, formatv erases types using a base class and calls the virtual function format() to format the objects. To avoid these vtables, refactor formatv to instead store function_refs to a functor (struct with overloaded operator()). This saves ~5kiB in vtables. Additionally, add a static assertion that a formatter is present instead of relying on errors due to missing templates. This requires a little change to MLIR's tgfmt to use a different name -- previously, the substitution failure on ArrayRef<> would cause the variadic function to be skipped, but a static assertion failure is not a substitution error. Also, simplify the code in FormatVariadicDetails to use if constexpr instead of template overloads with enable_if, making the code shorter and easier to read.
This commit is contained in:
parent
6b3433ae19
commit
3aec6a40bb
15 changed files with 127 additions and 220 deletions
|
|
@ -107,26 +107,26 @@ static llvm::Error FormatImpl(DataStack &data) {
|
|||
}
|
||||
using namespace llvm::support::detail;
|
||||
auto arg = data[data.size() - num_args + r.Index];
|
||||
auto format = [&](format_adapter &&adapter) {
|
||||
auto format = [&](FormatFunctorRef &&adapter) {
|
||||
llvm::FmtAlign Align(adapter, r.Where, r.Width, r.Pad);
|
||||
Align.format(os, r.Options);
|
||||
};
|
||||
|
||||
if (auto s = std::get_if<std::string>(&arg))
|
||||
format(build_format_adapter(s->c_str()));
|
||||
format(FormatFunctor(s->c_str()));
|
||||
else if (auto u = std::get_if<uint64_t>(&arg))
|
||||
format(build_format_adapter(u));
|
||||
format(FormatFunctor(u));
|
||||
else if (auto i = std::get_if<int64_t>(&arg))
|
||||
format(build_format_adapter(i));
|
||||
format(FormatFunctor(i));
|
||||
else if (auto valobj = std::get_if<ValueObjectSP>(&arg)) {
|
||||
if (!valobj->get())
|
||||
format(build_format_adapter("null object"));
|
||||
format(FormatFunctor("null object"));
|
||||
else
|
||||
format(build_format_adapter(valobj->get()->GetValueAsCString()));
|
||||
format(FormatFunctor(valobj->get()->GetValueAsCString()));
|
||||
} else if (auto type = std::get_if<CompilerType>(&arg))
|
||||
format(build_format_adapter(type->GetDisplayTypeName()));
|
||||
format(FormatFunctor(type->GetDisplayTypeName()));
|
||||
else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&arg))
|
||||
format(build_format_adapter(toString(*sel)));
|
||||
format(FormatFunctor(toString(*sel)));
|
||||
}
|
||||
data.Push(s);
|
||||
return llvm::Error::success();
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public:
|
|||
explicit GuidAdapter(ArrayRef<uint8_t> Guid);
|
||||
explicit GuidAdapter(StringRef Guid);
|
||||
|
||||
void format(raw_ostream &Stream, StringRef Style) override;
|
||||
void format(raw_ostream &Stream, StringRef Style);
|
||||
};
|
||||
|
||||
} // end namespace detail
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ struct EndianAdapter final
|
|||
explicit EndianAdapter(EndianType &&Item)
|
||||
: FormatAdapter<EndianType>(std::move(Item)) {}
|
||||
|
||||
void format(llvm::raw_ostream &Stream, StringRef Style) override {
|
||||
void format(llvm::raw_ostream &Stream, StringRef Style) {
|
||||
format_provider<T>::format(static_cast<T>(this->Item), Stream, Style);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
namespace llvm {
|
||||
template <typename T>
|
||||
class FormatAdapter : public support::detail::format_adapter {
|
||||
template <typename T> class FormatAdapter {
|
||||
protected:
|
||||
explicit FormatAdapter(T &&Item) : Item(std::forward<T>(Item)) {}
|
||||
|
||||
|
|
@ -36,8 +35,8 @@ public:
|
|||
: FormatAdapter<T>(std::forward<T>(Item)), Where(Where), Amount(Amount),
|
||||
Fill(Fill) {}
|
||||
|
||||
void format(llvm::raw_ostream &Stream, StringRef Style) override {
|
||||
auto Adapter = detail::build_format_adapter(std::forward<T>(this->Item));
|
||||
void format(llvm::raw_ostream &Stream, StringRef Style) {
|
||||
auto Adapter = detail::FormatFunctor(std::forward<T>(this->Item));
|
||||
FmtAlign(Adapter, Where, Amount, Fill).format(Stream, Style);
|
||||
}
|
||||
};
|
||||
|
|
@ -50,10 +49,10 @@ public:
|
|||
PadAdapter(T &&Item, size_t Left, size_t Right)
|
||||
: FormatAdapter<T>(std::forward<T>(Item)), Left(Left), Right(Right) {}
|
||||
|
||||
void format(llvm::raw_ostream &Stream, StringRef Style) override {
|
||||
auto Adapter = detail::build_format_adapter(std::forward<T>(this->Item));
|
||||
void format(llvm::raw_ostream &Stream, StringRef Style) {
|
||||
auto Adapter = detail::FormatFunctor(std::forward<T>(this->Item));
|
||||
Stream.indent(Left);
|
||||
Adapter.format(Stream, Style);
|
||||
Adapter(Stream, Style);
|
||||
Stream.indent(Right);
|
||||
}
|
||||
};
|
||||
|
|
@ -65,10 +64,10 @@ public:
|
|||
RepeatAdapter(T &&Item, size_t Count)
|
||||
: FormatAdapter<T>(std::forward<T>(Item)), Count(Count) {}
|
||||
|
||||
void format(llvm::raw_ostream &Stream, StringRef Style) override {
|
||||
auto Adapter = detail::build_format_adapter(std::forward<T>(this->Item));
|
||||
void format(llvm::raw_ostream &Stream, StringRef Style) {
|
||||
auto Adapter = detail::FormatFunctor(std::forward<T>(this->Item));
|
||||
for (size_t I = 0; I < Count; ++I) {
|
||||
Adapter.format(Stream, Style);
|
||||
Adapter(Stream, Style);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -77,10 +76,8 @@ class ErrorAdapter : public FormatAdapter<Error> {
|
|||
public:
|
||||
ErrorAdapter(Error &&Item) : FormatAdapter(std::move(Item)) {}
|
||||
ErrorAdapter(ErrorAdapter &&) = default;
|
||||
~ErrorAdapter() override { consumeError(std::move(Item)); }
|
||||
void format(llvm::raw_ostream &Stream, StringRef Style) override {
|
||||
Stream << Item;
|
||||
}
|
||||
~ErrorAdapter() { consumeError(std::move(Item)); }
|
||||
void format(llvm::raw_ostream &Stream, StringRef Style) { Stream << Item; }
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace support
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ enum class AlignStyle { Left, Center, Right };
|
|||
/// Helper class to format to a \p Width wide field, with alignment \p Where
|
||||
/// within that field.
|
||||
struct FmtAlign {
|
||||
support::detail::format_adapter &Adapter;
|
||||
support::detail::FormatFunctorRef Adapter;
|
||||
AlignStyle Where;
|
||||
unsigned Width;
|
||||
char Fill;
|
||||
|
||||
FmtAlign(support::detail::format_adapter &Adapter, AlignStyle Where,
|
||||
FmtAlign(support::detail::FormatFunctorRef Adapter, AlignStyle Where,
|
||||
unsigned Width, char Fill = ' ')
|
||||
: Adapter(Adapter), Where(Where), Width(Width), Fill(Fill) {}
|
||||
|
||||
|
|
@ -35,13 +35,13 @@ struct FmtAlign {
|
|||
// TODO: Make the format method return the number of bytes written, that
|
||||
// way we can also skip the intermediate stream for left-aligned output.
|
||||
if (Width == 0) {
|
||||
Adapter.format(S, Options);
|
||||
Adapter(S, Options);
|
||||
return;
|
||||
}
|
||||
SmallString<64> Item;
|
||||
raw_svector_ostream Stream(Item);
|
||||
|
||||
Adapter.format(Stream, Options);
|
||||
Adapter(Stream, Options);
|
||||
if (Width <= Item.size()) {
|
||||
S << Item;
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -325,18 +325,6 @@ struct format_provider<
|
|||
}
|
||||
};
|
||||
|
||||
namespace support {
|
||||
namespace detail {
|
||||
template <typename IterT>
|
||||
using IterValue = typename std::iterator_traits<IterT>::value_type;
|
||||
|
||||
template <typename IterT>
|
||||
struct range_item_has_provider
|
||||
: public std::bool_constant<
|
||||
!support::detail::uses_missing_provider<IterValue<IterT>>::value> {};
|
||||
} // namespace detail
|
||||
} // namespace support
|
||||
|
||||
/// Implementation of format_provider<T> for ranges.
|
||||
///
|
||||
/// This will print an arbitrary range as a delimited sequence of items.
|
||||
|
|
@ -399,8 +387,6 @@ template <typename IterT> class format_provider<llvm::iterator_range<IterT>> {
|
|||
}
|
||||
|
||||
public:
|
||||
static_assert(support::detail::range_item_has_provider<IterT>::value,
|
||||
"Range value_type does not have a format provider!");
|
||||
static void format(const llvm::iterator_range<IterT> &V,
|
||||
llvm::raw_ostream &Stream, StringRef Style) {
|
||||
StringRef Sep;
|
||||
|
|
@ -409,14 +395,14 @@ public:
|
|||
auto Begin = V.begin();
|
||||
auto End = V.end();
|
||||
if (Begin != End) {
|
||||
auto Adapter = support::detail::build_format_adapter(*Begin);
|
||||
Adapter.format(Stream, ArgStyle);
|
||||
auto Adapter = support::detail::FormatFunctor(*Begin);
|
||||
Adapter(Stream, ArgStyle);
|
||||
++Begin;
|
||||
}
|
||||
while (Begin != End) {
|
||||
Stream << Sep;
|
||||
auto Adapter = support::detail::build_format_adapter(*Begin);
|
||||
Adapter.format(Stream, ArgStyle);
|
||||
auto Adapter = support::detail::FormatFunctor(*Begin);
|
||||
Adapter(Stream, ArgStyle);
|
||||
++Begin;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,11 +65,11 @@ struct ReplacementItem {
|
|||
class formatv_object_base {
|
||||
protected:
|
||||
StringRef Fmt;
|
||||
ArrayRef<support::detail::format_adapter *> Adapters;
|
||||
ArrayRef<support::detail::FormatFunctorRef> Adapters;
|
||||
bool Validate;
|
||||
|
||||
formatv_object_base(StringRef Fmt,
|
||||
ArrayRef<support::detail::format_adapter *> Adapters,
|
||||
ArrayRef<support::detail::FormatFunctorRef> Adapters,
|
||||
bool Validate)
|
||||
: Fmt(Fmt), Adapters(Adapters), Validate(Validate) {}
|
||||
|
||||
|
|
@ -89,9 +89,9 @@ public:
|
|||
continue;
|
||||
}
|
||||
|
||||
auto *W = Adapters[R.Index];
|
||||
auto &W = Adapters[R.Index];
|
||||
|
||||
FmtAlign Align(*W, R.Where, R.Width, R.Pad);
|
||||
FmtAlign Align(W, R.Where, R.Width, R.Pad);
|
||||
Align.format(S, R.Options);
|
||||
}
|
||||
}
|
||||
|
|
@ -122,21 +122,19 @@ template <typename Tuple> class formatv_object : public formatv_object_base {
|
|||
// of the parameters, we have to own the storage for the parameters here, and
|
||||
// have the base class store type-erased pointers into this tuple.
|
||||
Tuple Parameters;
|
||||
std::array<support::detail::format_adapter *, std::tuple_size<Tuple>::value>
|
||||
std::array<support::detail::FormatFunctorRef, std::tuple_size<Tuple>::value>
|
||||
ParameterPointers;
|
||||
|
||||
// The parameters are stored in a std::tuple, which does not provide runtime
|
||||
// indexing capabilities. In order to enable runtime indexing, we use this
|
||||
// structure to put the parameters into a std::array. Since the parameters
|
||||
// are not all the same type, we use some type-erasure by wrapping the
|
||||
// parameters in a template class that derives from a non-template superclass.
|
||||
// Essentially, we are converting a std::tuple<Derived<Ts...>> to a
|
||||
// std::array<Base*>.
|
||||
// parameters in a template class and refer to them using function_refs.
|
||||
struct create_adapters {
|
||||
template <typename... Ts>
|
||||
std::array<support::detail::format_adapter *, std::tuple_size<Tuple>::value>
|
||||
std::array<support::detail::FormatFunctorRef, std::tuple_size<Tuple>::value>
|
||||
operator()(Ts &...Items) {
|
||||
return {{&Items...}};
|
||||
return {{Items...}};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -248,7 +246,7 @@ public:
|
|||
template <typename... Ts>
|
||||
inline auto formatv(bool Validate, const char *Fmt, Ts &&...Vals) {
|
||||
auto Params = std::make_tuple(
|
||||
support::detail::build_format_adapter(std::forward<Ts>(Vals))...);
|
||||
support::detail::FormatFunctor(std::forward<Ts>(Vals))...);
|
||||
return formatv_object<decltype(Params)>(Fmt, std::move(Params), Validate);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#define LLVM_SUPPORT_FORMATVARIADICDETAILS_H
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/STLForwardCompat.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
|
@ -22,134 +23,58 @@ class Error;
|
|||
|
||||
namespace support {
|
||||
namespace detail {
|
||||
class LLVM_ABI format_adapter {
|
||||
virtual void anchor();
|
||||
|
||||
protected:
|
||||
virtual ~format_adapter() = default;
|
||||
using FormatFunctorRef = function_ref<void(llvm::raw_ostream &, StringRef)>;
|
||||
|
||||
public:
|
||||
virtual void format(raw_ostream &S, StringRef Options) = 0;
|
||||
};
|
||||
|
||||
template <typename T> class provider_format_adapter : public format_adapter {
|
||||
T Item;
|
||||
|
||||
public:
|
||||
explicit provider_format_adapter(T &&Item) : Item(std::forward<T>(Item)) {}
|
||||
|
||||
void format(llvm::raw_ostream &S, StringRef Options) override {
|
||||
format_provider<std::decay_t<T>>::format(Item, S, Options);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class stream_operator_format_adapter : public format_adapter {
|
||||
T Item;
|
||||
|
||||
public:
|
||||
explicit stream_operator_format_adapter(T &&Item)
|
||||
: Item(std::forward<T>(Item)) {}
|
||||
|
||||
void format(llvm::raw_ostream &S, StringRef) override { S << Item; }
|
||||
};
|
||||
|
||||
template <typename T> class missing_format_adapter;
|
||||
|
||||
// Test if format_provider<T> is defined on T and contains a member function
|
||||
// with the signature:
|
||||
// static void format(const T&, raw_stream &, StringRef);
|
||||
//
|
||||
template <class T> class has_FormatProvider {
|
||||
public:
|
||||
using Decayed = std::decay_t<T>;
|
||||
using Signature_format = void (*)(const Decayed &, llvm::raw_ostream &,
|
||||
StringRef);
|
||||
|
||||
template <typename U> using check = SameType<Signature_format, &U::format>;
|
||||
|
||||
static constexpr bool value =
|
||||
llvm::is_detected<check, llvm::format_provider<Decayed>>::value;
|
||||
};
|
||||
|
||||
// Test if raw_ostream& << T -> raw_ostream& is findable via ADL.
|
||||
template <class T> class has_StreamOperator {
|
||||
public:
|
||||
using ConstRefT = const std::decay_t<T> &;
|
||||
|
||||
template <typename U>
|
||||
static auto test(int)
|
||||
-> std::is_same<decltype(std::declval<llvm::raw_ostream &>()
|
||||
<< std::declval<U>()),
|
||||
llvm::raw_ostream &>;
|
||||
|
||||
template <typename U> static auto test(...) -> std::false_type;
|
||||
|
||||
static constexpr bool value = decltype(test<ConstRefT>(0))::value;
|
||||
};
|
||||
|
||||
// Simple template that decides whether a type T should use the member-function
|
||||
// based format() invocation.
|
||||
template <typename T>
|
||||
struct uses_format_member
|
||||
: public std::is_base_of<format_adapter, std::remove_reference_t<T>> {};
|
||||
|
||||
// Simple template that decides whether a type T should use the format_provider
|
||||
// based format() invocation. The member function takes priority, so this test
|
||||
// will only be true if there is not ALSO a format member.
|
||||
template <typename T>
|
||||
struct uses_format_provider
|
||||
: public std::bool_constant<!uses_format_member<T>::value &&
|
||||
has_FormatProvider<T>::value> {};
|
||||
|
||||
// Simple template that decides whether a type T should use the operator<<
|
||||
// based format() invocation. This takes last priority.
|
||||
template <typename T>
|
||||
struct uses_stream_operator
|
||||
: public std::bool_constant<!uses_format_member<T>::value &&
|
||||
!uses_format_provider<T>::value &&
|
||||
has_StreamOperator<T>::value> {};
|
||||
|
||||
// Simple template that decides whether a type T has neither a member-function
|
||||
// nor format_provider based implementation that it can use. Mostly used so
|
||||
// that the compiler spits out a nice diagnostic when a type with no format
|
||||
// implementation can be located.
|
||||
template <typename T>
|
||||
struct uses_missing_provider
|
||||
: public std::bool_constant<!uses_format_member<T>::value &&
|
||||
!uses_format_provider<T>::value &&
|
||||
!uses_stream_operator<T>::value> {};
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<uses_format_member<T>::value, T>
|
||||
build_format_adapter(T &&Item) {
|
||||
return std::forward<T>(Item);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<uses_format_provider<T>::value, provider_format_adapter<T>>
|
||||
build_format_adapter(T &&Item) {
|
||||
return provider_format_adapter<T>(std::forward<T>(Item));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<uses_stream_operator<T>::value,
|
||||
stream_operator_format_adapter<T>>
|
||||
build_format_adapter(T &&Item) {
|
||||
// If the caller passed an Error by value, then stream_operator_format_adapter
|
||||
// would be responsible for consuming it.
|
||||
// Make the caller opt into this by calling fmt_consume().
|
||||
template <typename T> class FormatFunctor {
|
||||
// If the caller passed an Error by value, then we would be responsible for
|
||||
// consuming it. Make the caller opt into this by calling fmt_consume().
|
||||
static_assert(
|
||||
!std::is_same_v<llvm::Error, std::remove_cv_t<T>>,
|
||||
"llvm::Error-by-value must be wrapped in fmt_consume() for formatv");
|
||||
return stream_operator_format_adapter<T>(std::forward<T>(Item));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<uses_missing_provider<T>::value, missing_format_adapter<T>>
|
||||
build_format_adapter(T &&) {
|
||||
return missing_format_adapter<T>();
|
||||
}
|
||||
T Item;
|
||||
|
||||
using DecayedT = std::decay_t<T>;
|
||||
using Signature_format = void (*)(const DecayedT &, llvm::raw_ostream &,
|
||||
StringRef);
|
||||
|
||||
template <typename U>
|
||||
using MemberFormatCheck = decltype(std::declval<U>().format(
|
||||
std::declval<llvm::raw_ostream &>(), std::declval<llvm::StringRef>()));
|
||||
template <typename U>
|
||||
using StaticFormatCheck = SameType<Signature_format, &U::format>;
|
||||
template <typename U>
|
||||
using StreamCheck = std::is_same<decltype(std::declval<llvm::raw_ostream &>()
|
||||
<< std::declval<U>()),
|
||||
llvm::raw_ostream &>;
|
||||
|
||||
public:
|
||||
static constexpr bool HasMemberProvider =
|
||||
llvm::is_detected<MemberFormatCheck, DecayedT>::value;
|
||||
static constexpr bool HasFormatProvider =
|
||||
llvm::is_detected<StaticFormatCheck,
|
||||
llvm::format_provider<DecayedT>>::value;
|
||||
static constexpr bool HasStreamProvider =
|
||||
llvm::is_detected<StreamCheck, DecayedT>::value;
|
||||
|
||||
static_assert(HasMemberProvider || HasFormatProvider || HasStreamProvider,
|
||||
"type has no format provider");
|
||||
|
||||
explicit FormatFunctor(T &&Item) : Item(std::forward<T>(Item)) {}
|
||||
|
||||
void operator()(llvm::raw_ostream &S, StringRef Options) {
|
||||
if constexpr (HasMemberProvider)
|
||||
Item.format(S, Options);
|
||||
else if constexpr (HasFormatProvider)
|
||||
format_provider<DecayedT>::format(Item, S, Options);
|
||||
else if constexpr (HasStreamProvider)
|
||||
S << Item;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> FormatFunctor(T &&) -> FormatFunctor<T>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace support
|
||||
} // namespace llvm
|
||||
|
|
|
|||
|
|
@ -222,5 +222,3 @@ formatv_object_base::parseFormatString(StringRef Fmt, size_t NumArgs,
|
|||
#endif // ENABLE_VALIDATION
|
||||
return Replacements;
|
||||
}
|
||||
|
||||
void support::detail::format_adapter::anchor() {}
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ struct format_xray_record : public FormatAdapter<XRayRecord> {
|
|||
explicit format_xray_record(XRayRecord record,
|
||||
const FuncIdConversionHelper &conv)
|
||||
: FormatAdapter<XRayRecord>(std::move(record)), Converter(&conv) {}
|
||||
void format(raw_ostream &Stream, StringRef Style) override {
|
||||
void format(raw_ostream &Stream, StringRef Style) {
|
||||
Stream << formatv(
|
||||
"{FuncId: \"{0}\", ThreadId: \"{1}\", RecordType: \"{2}\"}",
|
||||
Converter->SymbolOrNumber(Item.FuncId), Item.TId,
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ TEST(TwineTest, LazyEvaluation) {
|
|||
explicit formatter(int &Count) : FormatAdapter(0), Count(Count) {}
|
||||
int &Count;
|
||||
|
||||
void format(raw_ostream &OS, StringRef Style) override { ++Count; }
|
||||
void format(raw_ostream &OS, StringRef Style) { ++Count; }
|
||||
};
|
||||
|
||||
int Count = 0;
|
||||
|
|
|
|||
|
|
@ -18,22 +18,28 @@ using namespace llvm;
|
|||
namespace {
|
||||
struct Format : public FormatAdapter<int> {
|
||||
Format(int N) : FormatAdapter<int>(std::move(N)) {}
|
||||
void format(raw_ostream &OS, StringRef Opt) override { OS << "Format"; }
|
||||
void format(raw_ostream &OS, StringRef Opt) { OS << "Format"; }
|
||||
};
|
||||
|
||||
using support::detail::uses_format_member;
|
||||
using support::detail::uses_missing_provider;
|
||||
using support::detail::FormatFunctor;
|
||||
|
||||
static_assert(uses_format_member<Format>::value, "");
|
||||
static_assert(uses_format_member<Format &>::value, "");
|
||||
static_assert(uses_format_member<Format &&>::value, "");
|
||||
static_assert(uses_format_member<const Format>::value, "");
|
||||
static_assert(uses_format_member<const Format &>::value, "");
|
||||
static_assert(uses_format_member<const volatile Format>::value, "");
|
||||
static_assert(uses_format_member<const volatile Format &>::value, "");
|
||||
static_assert(FormatFunctor<Format>::HasMemberProvider, "");
|
||||
static_assert(FormatFunctor<Format &>::HasMemberProvider, "");
|
||||
static_assert(FormatFunctor<Format &&>::HasMemberProvider, "");
|
||||
static_assert(FormatFunctor<const Format>::HasMemberProvider, "");
|
||||
static_assert(FormatFunctor<const Format &>::HasMemberProvider, "");
|
||||
static_assert(FormatFunctor<const volatile Format>::HasMemberProvider, "");
|
||||
static_assert(FormatFunctor<const volatile Format &>::HasMemberProvider, "");
|
||||
|
||||
struct NoFormat {};
|
||||
static_assert(uses_missing_provider<NoFormat>::value, "");
|
||||
struct StreamFormat {};
|
||||
// Silence warning about never-called function.
|
||||
[[maybe_unused]] raw_ostream &operator<<(raw_ostream &, const StreamFormat &);
|
||||
static_assert(!FormatFunctor<StreamFormat>::HasMemberProvider, "");
|
||||
static_assert(!FormatFunctor<StreamFormat>::HasFormatProvider, "");
|
||||
static_assert(FormatFunctor<StreamFormat>::HasStreamProvider, "");
|
||||
static_assert(!FormatFunctor<int>::HasMemberProvider, "");
|
||||
static_assert(FormatFunctor<int>::HasFormatProvider, "");
|
||||
static_assert(FormatFunctor<int>::HasStreamProvider, "");
|
||||
}
|
||||
|
||||
// Helper to parse format string with no validation.
|
||||
|
|
@ -780,7 +786,7 @@ TEST(FormatVariadicTest, Adapter) {
|
|||
class Negative : public FormatAdapter<int> {
|
||||
public:
|
||||
explicit Negative(int N) : FormatAdapter<int>(std::move(N)) {}
|
||||
void format(raw_ostream &S, StringRef Options) override { S << -Item; }
|
||||
void format(raw_ostream &S, StringRef Options) { S << -Item; }
|
||||
};
|
||||
|
||||
EXPECT_EQ("-7", formatv("{0}", Negative(7)).str());
|
||||
|
|
|
|||
|
|
@ -127,15 +127,15 @@ protected:
|
|||
// std::vector<Base*>.
|
||||
struct CreateAdapters {
|
||||
template <typename... Ts>
|
||||
std::vector<llvm::support::detail::format_adapter *>
|
||||
std::vector<llvm::support::detail::FormatFunctorRef>
|
||||
operator()(Ts &...items) {
|
||||
return std::vector<llvm::support::detail::format_adapter *>{&items...};
|
||||
return std::vector<llvm::support::detail::FormatFunctorRef>{items...};
|
||||
}
|
||||
};
|
||||
|
||||
StringRef fmt;
|
||||
const FmtContext *context;
|
||||
std::vector<llvm::support::detail::format_adapter *> adapters;
|
||||
std::vector<llvm::support::detail::FormatFunctorRef> adapters;
|
||||
std::vector<FmtReplacement> replacements;
|
||||
|
||||
public:
|
||||
|
|
@ -200,8 +200,7 @@ public:
|
|||
|
||||
class FmtStrVecObject : public FmtObjectBase {
|
||||
public:
|
||||
using StrFormatAdapter = decltype(llvm::support::detail::build_format_adapter(
|
||||
std::declval<std::string>()));
|
||||
using StrFormatAdapter = llvm::support::detail::FormatFunctor<std::string>;
|
||||
|
||||
FmtStrVecObject(StringRef fmt, const FmtContext *ctx,
|
||||
ArrayRef<std::string> params);
|
||||
|
|
@ -254,19 +253,19 @@ private:
|
|||
/// in C++ code generation.
|
||||
template <typename... Ts>
|
||||
inline auto tgfmt(StringRef fmt, const FmtContext *ctx, Ts &&...vals)
|
||||
-> FmtObject<
|
||||
decltype(std::make_tuple(llvm::support::detail::build_format_adapter(
|
||||
std::forward<Ts>(vals))...))> {
|
||||
-> FmtObject<decltype(std::make_tuple(
|
||||
llvm::support::detail::FormatFunctor(std::forward<Ts>(vals))...))> {
|
||||
using ParamTuple = decltype(std::make_tuple(
|
||||
llvm::support::detail::build_format_adapter(std::forward<Ts>(vals))...));
|
||||
llvm::support::detail::FormatFunctor(std::forward<Ts>(vals))...));
|
||||
return FmtObject<ParamTuple>(
|
||||
fmt, ctx,
|
||||
std::make_tuple(llvm::support::detail::build_format_adapter(
|
||||
std::forward<Ts>(vals))...));
|
||||
std::make_tuple(
|
||||
llvm::support::detail::FormatFunctor(std::forward<Ts>(vals))...));
|
||||
}
|
||||
|
||||
inline FmtStrVecObject tgfmt(StringRef fmt, const FmtContext *ctx,
|
||||
ArrayRef<std::string> params) {
|
||||
/// Like tgfmt, but take replacement parameters as an array of strings instead.
|
||||
inline FmtStrVecObject tgfmtv(StringRef fmt, const FmtContext *ctx,
|
||||
ArrayRef<std::string> params) {
|
||||
return FmtStrVecObject(fmt, ctx, params);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -183,8 +183,7 @@ void FmtObjectBase::format(raw_ostream &s) const {
|
|||
range = range.drop_front(repl.index);
|
||||
if (repl.end != FmtReplacement::kUnset)
|
||||
range = range.drop_back(adapters.size() - repl.end);
|
||||
llvm::interleaveComma(range, s,
|
||||
[&](auto &x) { x->format(s, /*Options=*/""); });
|
||||
llvm::interleaveComma(range, s, [&](auto &x) { x(s, /*Options=*/""); });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -194,7 +193,7 @@ void FmtObjectBase::format(raw_ostream &s) const {
|
|||
s << repl.spec << kMarkerForNoSubst;
|
||||
continue;
|
||||
}
|
||||
adapters[repl.index]->format(s, /*Options=*/"");
|
||||
adapters[repl.index](s, /*Options=*/"");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,17 +202,16 @@ FmtStrVecObject::FmtStrVecObject(StringRef fmt, const FmtContext *ctx,
|
|||
: FmtObjectBase(fmt, ctx, params.size()) {
|
||||
parameters.reserve(params.size());
|
||||
for (std::string p : params)
|
||||
parameters.push_back(
|
||||
llvm::support::detail::build_format_adapter(std::move(p)));
|
||||
parameters.emplace_back(std::move(p));
|
||||
|
||||
adapters.reserve(parameters.size());
|
||||
for (auto &p : parameters)
|
||||
adapters.push_back(&p);
|
||||
adapters.push_back(p);
|
||||
}
|
||||
|
||||
FmtStrVecObject::FmtStrVecObject(FmtStrVecObject &&that)
|
||||
: FmtObjectBase(std::move(that)), parameters(std::move(that.parameters)) {
|
||||
adapters.reserve(parameters.size());
|
||||
for (auto &p : parameters)
|
||||
adapters.push_back(&p);
|
||||
adapters.push_back(p);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -524,8 +524,8 @@ void PatternEmitter::emitNativeCodeMatch(DagNode tree, StringRef opName,
|
|||
"passing the defining Operation");
|
||||
|
||||
auto nativeCodeCall = std::string(
|
||||
tgfmt(fmt, &fmtCtx.addSubst("_loc", locToUse).withSelf(opName.str()),
|
||||
static_cast<ArrayRef<std::string>>(capture)));
|
||||
tgfmtv(fmt, &fmtCtx.addSubst("_loc", locToUse).withSelf(opName.str()),
|
||||
static_cast<ArrayRef<std::string>>(capture)));
|
||||
|
||||
emitMatchCheck(opName, formatv("!::mlir::failed({0})", nativeCodeCall),
|
||||
formatv("\"{0} return ::mlir::failure\"", nativeCodeCall));
|
||||
|
|
@ -1490,8 +1490,8 @@ std::string PatternEmitter::handleReplaceWithNativeCodeCall(DagNode tree,
|
|||
<< " replacement: " << attrs[i] << "\n");
|
||||
}
|
||||
|
||||
std::string symbol = tgfmt(fmt, &fmtCtx.addSubst("_loc", locToUse),
|
||||
static_cast<ArrayRef<std::string>>(attrs));
|
||||
std::string symbol = tgfmtv(fmt, &fmtCtx.addSubst("_loc", locToUse),
|
||||
static_cast<ArrayRef<std::string>>(attrs));
|
||||
|
||||
// In general, NativeCodeCall without naming binding don't need this. To
|
||||
// ensure void helper function has been correctly labeled, i.e., use
|
||||
|
|
|
|||
Loading…
Reference in a new issue