1 //===- Format.h - Utilities for String Format -------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file declares utilities for formatting strings. They are specially 10 // tailored to the needs of TableGen'ing op definitions and rewrite rules, 11 // so they are not expected to be used as widely applicable utilities. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #ifndef MLIR_TABLEGEN_FORMAT_H_ 16 #define MLIR_TABLEGEN_FORMAT_H_ 17 18 #include "mlir/Support/LLVM.h" 19 #include "llvm/ADT/DenseMap.h" 20 #include "llvm/ADT/StringMap.h" 21 #include "llvm/Support/FormatVariadic.h" 22 23 namespace mlir { 24 namespace tblgen { 25 26 /// Format context containing substitutions for special placeholders. 27 /// 28 /// This context divides special placeholders into two categories: builtin ones 29 /// and custom ones. 30 /// 31 /// Builtin placeholders are baked into `FmtContext` and each one of them has a 32 /// dedicated setter. They can be used in all dialects. Their names follow the 33 /// convention of `$_<name>`. The rationale of the leading underscore is to 34 /// avoid confusion and name collision: op arguments/attributes/results are 35 /// named as $<name>, and we can potentially support referencing those entities 36 /// directly in the format template in the future. 37 // 38 /// Custom ones are registered by dialect-specific TableGen backends and use the 39 /// same unified setter. 40 class FmtContext { 41 public: 42 // Placeholder kinds 43 enum class PHKind : char { 44 None, 45 Custom, // For custom placeholders 46 Builder, // For the $_builder placeholder 47 Op, // For the $_op placeholder 48 Self, // For the $_self placeholder 49 }; 50 51 FmtContext() = default; 52 53 // Setter for custom placeholders 54 FmtContext &addSubst(StringRef placeholder, Twine subst); 55 56 // Setters for builtin placeholders 57 FmtContext &withBuilder(Twine subst); 58 FmtContext &withOp(Twine subst); 59 FmtContext &withSelf(Twine subst); 60 61 Optional<StringRef> getSubstFor(PHKind placeholder) const; 62 Optional<StringRef> getSubstFor(StringRef placeholder) const; 63 64 static PHKind getPlaceHolderKind(StringRef str); 65 66 private: 67 struct PHKindInfo : DenseMapInfo<PHKind> { 68 using CharInfo = DenseMapInfo<char>; 69 getEmptyKeyPHKindInfo70 static inline PHKind getEmptyKey() { 71 return static_cast<PHKind>(CharInfo::getEmptyKey()); 72 } getTombstoneKeyPHKindInfo73 static inline PHKind getTombstoneKey() { 74 return static_cast<PHKind>(CharInfo::getTombstoneKey()); 75 } getHashValuePHKindInfo76 static unsigned getHashValue(const PHKind &val) { 77 return CharInfo::getHashValue(static_cast<char>(val)); 78 } 79 isEqualPHKindInfo80 static bool isEqual(const PHKind &lhs, const PHKind &rhs) { 81 return lhs == rhs; 82 } 83 }; 84 85 llvm::SmallDenseMap<PHKind, std::string, 4, PHKindInfo> builtinSubstMap; 86 llvm::StringMap<std::string> customSubstMap; 87 }; 88 89 /// Struct representing a replacement segment for the formatted string. It can 90 /// be a segment of the formatting template (for `Literal`) or a replacement 91 /// parameter (for `PositionalPH` and `SpecialPH`). 92 struct FmtReplacement { 93 enum class Type { Empty, Literal, PositionalPH, SpecialPH }; 94 95 FmtReplacement() = default; FmtReplacementFmtReplacement96 explicit FmtReplacement(StringRef literal) 97 : type(Type::Literal), spec(literal) {} FmtReplacementFmtReplacement98 FmtReplacement(StringRef spec, size_t index) 99 : type(Type::PositionalPH), spec(spec), index(index) {} FmtReplacementFmtReplacement100 FmtReplacement(StringRef spec, FmtContext::PHKind placeholder) 101 : type(Type::SpecialPH), spec(spec), placeholder(placeholder) {} 102 103 Type type = Type::Empty; 104 StringRef spec; 105 size_t index = 0; 106 FmtContext::PHKind placeholder = FmtContext::PHKind::None; 107 }; 108 109 class FmtObjectBase { 110 private: 111 static std::pair<FmtReplacement, StringRef> splitFmtSegment(StringRef fmt); 112 static std::vector<FmtReplacement> parseFormatString(StringRef fmt); 113 114 protected: 115 // The parameters are stored in a std::tuple, which does not provide runtime 116 // indexing capabilities. In order to enable runtime indexing, we use this 117 // structure to put the parameters into a std::vector. Since the parameters 118 // are not all the same type, we use some type-erasure by wrapping the 119 // parameters in a template class that derives from a non-template superclass. 120 // Essentially, we are converting a std::tuple<Derived<Ts...>> to a 121 // std::vector<Base*>. 122 struct CreateAdapters { 123 template <typename... Ts> operatorCreateAdapters124 std::vector<llvm::detail::format_adapter *> operator()(Ts &... items) { 125 return std::vector<llvm::detail::format_adapter *>{&items...}; 126 } 127 }; 128 129 StringRef fmt; 130 const FmtContext *context; 131 std::vector<llvm::detail::format_adapter *> adapters; 132 std::vector<FmtReplacement> replacements; 133 134 public: FmtObjectBase(StringRef fmt,const FmtContext * ctx,size_t numParams)135 FmtObjectBase(StringRef fmt, const FmtContext *ctx, size_t numParams) 136 : fmt(fmt), context(ctx), replacements(parseFormatString(fmt)) {} 137 138 FmtObjectBase(const FmtObjectBase &that) = delete; 139 FmtObjectBase(FmtObjectBase && that)140 FmtObjectBase(FmtObjectBase &&that) 141 : fmt(std::move(that.fmt)), context(that.context), 142 adapters(), // adapters are initialized by FmtObject 143 replacements(std::move(that.replacements)) {} 144 145 void format(llvm::raw_ostream &s) const; 146 str()147 std::string str() const { 148 std::string result; 149 llvm::raw_string_ostream s(result); 150 format(s); 151 return s.str(); 152 } 153 sstr()154 template <unsigned N> SmallString<N> sstr() const { 155 SmallString<N> result; 156 llvm::raw_svector_ostream s(result); 157 format(s); 158 return result; 159 } 160 161 template <unsigned N> operator SmallString<N>() const { return sstr<N>(); } 162 string()163 operator std::string() const { return str(); } 164 }; 165 166 template <typename Tuple> class FmtObject : public FmtObjectBase { 167 // Storage for the parameter adapters. Since the base class erases the type 168 // of the parameters, we have to own the storage for the parameters here, and 169 // have the base class store type-erased pointers into this tuple. 170 Tuple parameters; 171 172 public: FmtObject(StringRef fmt,const FmtContext * ctx,Tuple && params)173 FmtObject(StringRef fmt, const FmtContext *ctx, Tuple &¶ms) 174 : FmtObjectBase(fmt, ctx, std::tuple_size<Tuple>::value), 175 parameters(std::move(params)) { 176 adapters.reserve(std::tuple_size<Tuple>::value); 177 adapters = llvm::apply_tuple(CreateAdapters(), parameters); 178 } 179 180 FmtObject(FmtObject const &that) = delete; 181 FmtObject(FmtObject && that)182 FmtObject(FmtObject &&that) 183 : FmtObjectBase(std::move(that)), parameters(std::move(that.parameters)) { 184 adapters.reserve(that.adapters.size()); 185 adapters = llvm::apply_tuple(CreateAdapters(), parameters); 186 } 187 }; 188 189 /// Formats text by substituting placeholders in format string with replacement 190 /// parameters. 191 /// 192 /// There are two categories of placeholders accepted, both led by a '$' sign: 193 /// 194 /// 1. Positional placeholder: $[0-9]+ 195 /// 2. Special placeholder: $[a-zA-Z_][a-zA-Z0-9_]* 196 /// 197 /// Replacement parameters for positional placeholders are supplied as the 198 /// `vals` parameter pack with 1:1 mapping. That is, $0 will be replaced by the 199 /// first parameter in `vals`, $1 by the second one, and so on. Note that you 200 /// can use the positional placeholders in any order and repeat any times, for 201 /// example, "$2 $1 $1 $0" is accepted. 202 /// 203 /// Replacement parameters for special placeholders are supplied using the `ctx` 204 /// format context. 205 /// 206 /// The `fmt` is recorded as a `StringRef` inside the returned `FmtObject`. 207 /// The caller needs to make sure the underlying data is available when the 208 /// `FmtObject` is used. 209 /// 210 /// `ctx` accepts a nullptr if there is no special placeholder is used. 211 /// 212 /// If no substitution is provided for a placeholder or any error happens during 213 /// format string parsing or replacement, the placeholder will be outputted 214 /// as-is with an additional marker '<no-subst-found>', to aid debugging. 215 /// 216 /// To print a '$' literally, escape it with '$$'. 217 /// 218 /// This utility function is inspired by LLVM formatv(), with modifications 219 /// specially tailored for TableGen C++ generation usage: 220 /// 221 /// 1. This utility use '$' instead of '{' and '}' for denoting the placeholder 222 /// because '{' and '}' are frequently used in C++ code. 223 /// 2. This utility does not support format layout because it is rarely needed 224 /// in C++ code generation. 225 template <typename... Ts> 226 inline auto tgfmt(StringRef fmt, const FmtContext *ctx, Ts &&... vals) 227 -> FmtObject<decltype(std::make_tuple( 228 llvm::detail::build_format_adapter(std::forward<Ts>(vals))...))> { 229 using ParamTuple = decltype(std::make_tuple( 230 llvm::detail::build_format_adapter(std::forward<Ts>(vals))...)); 231 return FmtObject<ParamTuple>( 232 fmt, ctx, 233 std::make_tuple( 234 llvm::detail::build_format_adapter(std::forward<Ts>(vals))...)); 235 } 236 237 } // end namespace tblgen 238 } // end namespace mlir 239 240 #endif // MLIR_TABLEGEN_FORMAT_H_ 241