CIRCT  20.0.0git
PrettyPrinter.h
Go to the documentation of this file.
1 //===- PrettyPrinter.h - Pretty printing ----------------------------------===//
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 implements a pretty-printer.
10 // "PrettyPrinting", Derek C. Oppen, 1980.
11 // https://dx.doi.org/10.1145/357114.357115
12 //
13 // This was selected as it is linear in number of tokens O(n) and requires
14 // memory O(linewidth).
15 //
16 // See PrettyPrinter.cpp for more information.
17 //
18 //===----------------------------------------------------------------------===//
19 
20 #ifndef CIRCT_SUPPORT_PRETTYPRINTER_H
21 #define CIRCT_SUPPORT_PRETTYPRINTER_H
22 
23 #include "circt/Support/LLVM.h"
24 #include "llvm/ADT/SmallVector.h"
25 #include "llvm/ADT/StringRef.h"
26 #include "llvm/Support/ErrorHandling.h"
27 #include "llvm/Support/SaveAndRestore.h"
28 
29 #include <cstdint>
30 #include <deque>
31 #include <limits>
32 
33 namespace circt {
34 namespace pretty {
35 
36 //===----------------------------------------------------------------------===//
37 // Tokens
38 //===----------------------------------------------------------------------===//
39 
40 /// Style of breaking within a group:
41 /// - Consistent: all fits or all breaks.
42 /// - Inconsistent: best fit, break where needed.
43 /// - Never: force no breaking including nested groups.
45 
46 /// Style of indent when starting a group:
47 /// - Visual: offset is relative to current column.
48 /// - Block: offset is relative to current base indentation.
49 enum class IndentStyle { Visual, Block };
50 
51 class Token {
52 public:
53  enum class Kind { String, Break, Begin, End, Callback };
54 
55  struct TokenInfo {
56  Kind kind; // Common initial sequence.
57  };
58  struct StringInfo : public TokenInfo {
59  uint32_t len;
60  const char *str;
61  };
62  struct BreakInfo : public TokenInfo {
63  uint32_t spaces; // How many spaces to emit when not broken.
64  int32_t offset; // How many spaces to emit when broken.
65  bool neverbreak; // If set, behaves like break except this always 'fits'.
66  };
67  struct BeginInfo : public TokenInfo {
68  int32_t offset; // Adjust base indentation by this amount.
71  };
72  struct EndInfo : public TokenInfo {
73  // Nothing
74  };
75  // This can be used to associate a callback with the print event on the
76  // tokens stream. Since tokens strictly follow FIFO order on a queue, each
77  // CallbackToken can uniquely identify the data it is associated with, when it
78  // is added and popped from the queue.
79  struct CallbackInfo : public TokenInfo {};
80 
81 private:
82  union {
89  } data;
90 
91 protected:
92  template <Kind k, typename T>
93  static auto &getInfoImpl(T &t) {
94  if constexpr (k == Kind::String)
95  return t.data.stringInfo;
96  if constexpr (k == Kind::Break)
97  return t.data.breakInfo;
98  if constexpr (k == Kind::Begin)
99  return t.data.beginInfo;
100  if constexpr (k == Kind::End)
101  return t.data.endInfo;
102  if constexpr (k == Kind::Callback)
103  return t.data.callbackInfo;
104  llvm_unreachable("unhandled token kind");
105  }
106 
107  Token(Kind k) { data.info.kind = k; }
108 
109 public:
110  Kind getKind() const { return data.info.kind; }
111 };
112 
113 /// Helper class to CRTP-derive common functions.
114 template <class DerivedT, Token::Kind DerivedKind>
115 struct TokenBase : public Token {
116  static bool classof(const Token *t) { return t->getKind() == DerivedKind; }
117 
118 protected:
119  TokenBase() : Token(DerivedKind) {}
120 
121  using InfoType = std::remove_reference_t<std::invoke_result_t<
122  decltype(Token::getInfoImpl<DerivedKind, Token &>), Token &>>;
123 
124  InfoType &getInfoMut() { return Token::getInfoImpl<DerivedKind>(*this); }
125 
126  const InfoType &getInfo() const {
127  return Token::getInfoImpl<DerivedKind>(*this);
128  }
129 
130  template <typename... Args>
131  void initialize(Args &&...args) {
132  getInfoMut() = InfoType{{DerivedKind}, args...};
133  }
134 };
135 
136 /// Token types.
137 
138 struct StringToken : public TokenBase<StringToken, Token::Kind::String> {
139  StringToken(llvm::StringRef text) {
140  assert(text.size() == (uint32_t)text.size());
141  initialize((uint32_t)text.size(), text.data());
142  }
143  StringRef text() const { return StringRef(getInfo().str, getInfo().len); }
144 };
145 
146 struct BreakToken : public TokenBase<BreakToken, Token::Kind::Break> {
147  BreakToken(uint32_t spaces = 1, int32_t offset = 0, bool neverbreak = false) {
149  }
150  uint32_t spaces() const { return getInfo().spaces; }
151  int32_t offset() const { return getInfo().offset; }
152  bool neverbreak() const { return getInfo().neverbreak; }
153 };
154 
155 struct BeginToken : public TokenBase<BeginToken, Token::Kind::Begin> {
159  }
160  int32_t offset() const { return getInfo().offset; }
161  Breaks breaks() const { return getInfo().breaks; }
162  IndentStyle style() const { return getInfo().style; }
163 };
164 
165 struct EndToken : public TokenBase<EndToken, Token::Kind::End> {};
166 
167 struct CallbackToken : public TokenBase<CallbackToken, Token::Kind::Callback> {
168  CallbackToken() = default;
169 };
170 
171 //===----------------------------------------------------------------------===//
172 // PrettyPrinter
173 //===----------------------------------------------------------------------===//
174 
176 public:
177  /// Listener to Token storage events.
178  struct Listener {
179  virtual ~Listener();
180  /// No tokens referencing external memory are present.
181  virtual void clear(){};
182  /// Listener for print event.
183  virtual void print(){};
184  };
185 
186  /// PrettyPrinter for specified stream.
187  /// - margin: line width.
188  /// - baseIndent: always indent at least this much (starting 'indent' value).
189  /// - currentColumn: current column, used to calculate space remaining.
190  /// - maxStartingIndent: max column indentation starts at, must be >= margin.
191  PrettyPrinter(llvm::raw_ostream &os, uint32_t margin, uint32_t baseIndent = 0,
192  uint32_t currentColumn = 0,
193  uint32_t maxStartingIndent = kInfinity / 4,
194  Listener *listener = nullptr)
195  : space(margin - std::max(currentColumn, baseIndent)),
196  defaultFrame{baseIndent, PrintBreaks::Inconsistent}, indent(baseIndent),
198  os(os), listener(listener) {
200  assert(maxStartingIndent > baseIndent);
201  assert(margin > currentColumn);
202  // Ensure first print advances to at least baseIndent.
204  baseIndent > currentColumn ? baseIndent - currentColumn : 0;
205  }
207 
208  /// Add token for printing. In Oppen, this is "scan".
209  void add(Token t);
210 
211  /// Add a range of tokens.
212  template <typename R>
213  void addTokens(R &&tokens) {
214  // Don't invoke listener until range processed, we own it now.
215  {
216  llvm::SaveAndRestore<bool> save(donotClear, true);
217  for (Token &t : tokens)
218  add(t);
219  }
220  // Invoke it now if appropriate.
221  if (scanStack.empty())
222  clear();
223  }
224 
225  void eof();
226 
227  void setListener(Listener *newListener) { listener = newListener; };
228  auto *getListener() const { return listener; }
229 
230  static constexpr uint32_t kInfinity = (1U << 15) - 1;
231 
232 private:
233  /// Format token with tracked size.
234  struct FormattedToken {
235  Token token; /// underlying token
236  int32_t size; /// calculate size when positive.
237  };
238 
239  /// Breaking style for a printStack entry.
240  /// This is "Breaks" values with extra for "Fits".
241  /// Breaks::Never is "AlwaysFits" here.
243 
244  /// Printing information for active scope, stored in printStack.
245  struct PrintEntry {
246  uint32_t offset;
248  };
249 
250  /// Print out tokens we know sizes for, and drop from token buffer.
251  void advanceLeft();
252 
253  /// Break encountered, set sizes of begin/breaks in scanStack we now know.
254  void checkStack();
255 
256  /// Check if there's enough tokens to hit width, if so print.
257  /// If scan size is wider than line, it's infinity.
258  void checkStream();
259 
260  /// Print a token, maintaining printStack for context.
261  void print(const FormattedToken &f);
262 
263  /// Clear token buffer, scanStack must be empty.
264  void clear();
265 
266  /// Reset leftTotal and tokenOffset, rebase size data and scanStack indices.
267  void rebaseIfNeeded();
268 
269  /// Get current printing frame.
270  auto &getPrintFrame() {
271  return printStack.empty() ? defaultFrame : printStack.back();
272  }
273 
274  /// Characters left on this line.
275  int32_t space;
276 
277  /// Sizes: printed, enqueued
278  int32_t leftTotal;
279  int32_t rightTotal;
280 
281  /// Unprinted tokens, combination of 'token' and 'size' in Oppen.
282  std::deque<FormattedToken> tokens;
283  /// index of first token, for resolving scanStack entries.
284  uint32_t tokenOffset = 0;
285 
286  /// Stack of begin/break tokens, adjust by tokenOffset to index into tokens.
287  std::deque<uint32_t> scanStack;
288 
289  /// Stack of printing contexts (indentation + breaking behavior).
290  SmallVector<PrintEntry> printStack;
291 
292  /// Printing context when stack is empty.
294 
295  /// Number of "AlwaysFits" on print stack.
296  uint32_t alwaysFits = 0;
297 
298  /// Current indentation level
299  uint32_t indent;
300 
301  /// Whitespace to print before next, tracked to avoid trailing whitespace.
303 
304  /// Target line width.
305  const uint32_t margin;
306 
307  /// Maximum starting indentation level (default=kInfinity/4).
308  /// Useful to continue indentation past margin while still providing a limit
309  /// to avoid pathological output and for consumption by tools with limits.
310  const uint32_t maxStartingIndent;
311 
312  /// Output stream.
313  llvm::raw_ostream &os;
314 
315  /// Hook for Token storage events.
316  Listener *listener = nullptr;
317 
318  /// Flag to identify a state when the clear cannot be called.
319  bool donotClear = false;
320 
321  /// Threshold for walking scan state and "rebasing" totals/offsets.
322  static constexpr decltype(leftTotal) rebaseThreshold =
323  1UL << (std::numeric_limits<decltype(leftTotal)>::digits - 3);
324 };
325 
326 } // end namespace pretty
327 } // end namespace circt
328 
329 #endif // CIRCT_SUPPORT_PRETTYPRINTER_H
assert(baseType &&"element must be base type")
SmallVector< PrintEntry > printStack
Stack of printing contexts (indentation + breaking behavior).
void rebaseIfNeeded()
Reset leftTotal and tokenOffset, rebase size data and scanStack indices.
void add(Token t)
Add token for printing. In Oppen, this is "scan".
bool donotClear
Flag to identify a state when the clear cannot be called.
void addTokens(R &&tokens)
Add a range of tokens.
std::deque< FormattedToken > tokens
Unprinted tokens, combination of 'token' and 'size' in Oppen.
Listener * listener
Hook for Token storage events.
PrintBreaks
Breaking style for a printStack entry.
void setListener(Listener *newListener)
uint32_t indent
Current indentation level.
uint32_t tokenOffset
index of first token, for resolving scanStack entries.
const uint32_t maxStartingIndent
Maximum starting indentation level (default=kInfinity/4).
PrettyPrinter(llvm::raw_ostream &os, uint32_t margin, uint32_t baseIndent=0, uint32_t currentColumn=0, uint32_t maxStartingIndent=kInfinity/4, Listener *listener=nullptr)
PrettyPrinter for specified stream.
int32_t space
Characters left on this line.
static constexpr uint32_t kInfinity
llvm::raw_ostream & os
Output stream.
static constexpr decltype(leftTotal) rebaseThreshold
Threshold for walking scan state and "rebasing" totals/offsets.
void checkStack()
Break encountered, set sizes of begin/breaks in scanStack we now know.
auto & getPrintFrame()
Get current printing frame.
void print(const FormattedToken &f)
Print a token, maintaining printStack for context.
const uint32_t margin
Target line width.
uint32_t pendingIndentation
Whitespace to print before next, tracked to avoid trailing whitespace.
int32_t leftTotal
Sizes: printed, enqueued.
std::deque< uint32_t > scanStack
Stack of begin/break tokens, adjust by tokenOffset to index into tokens.
void clear()
Clear token buffer, scanStack must be empty.
void advanceLeft()
Print out tokens we know sizes for, and drop from token buffer.
uint32_t alwaysFits
Number of "AlwaysFits" on print stack.
void checkStream()
Check if there's enough tokens to hit width, if so print.
const PrintEntry defaultFrame
Printing context when stack is empty.
Kind getKind() const
union circt::pretty::Token::@5 data
CallbackInfo callbackInfo
Definition: PrettyPrinter.h:88
static auto & getInfoImpl(T &t)
Definition: PrettyPrinter.h:93
IndentStyle
Style of indent when starting a group:
Definition: PrettyPrinter.h:49
Breaks
Style of breaking within a group:
Definition: PrettyPrinter.h:44
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
IndentStyle style() const
BeginToken(int32_t offset=2, Breaks breaks=Breaks::Inconsistent, IndentStyle style=IndentStyle::Visual)
BreakToken(uint32_t spaces=1, int32_t offset=0, bool neverbreak=false)
uint32_t spaces() const
Format token with tracked size.
Listener to Token storage events.
virtual void clear()
No tokens referencing external memory are present.
virtual ~Listener()
Destructor, anchor.
virtual void print()
Listener for print event.
Printing information for active scope, stored in printStack.
StringRef text() const
StringToken(llvm::StringRef text)
Helper class to CRTP-derive common functions.
const InfoType & getInfo() const
std::remove_reference_t< std::invoke_result_t< decltype(Token::getInfoImpl< DerivedKind, Token & >), Token & > > InfoType
void initialize(Args &&...args)
static bool classof(const Token *t)