• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &&params)
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