Skip to content

Commit 9b85152

Browse files
committed
Add indented raw_ostream class
Class simplifies keeping track of the indentation while emitting. For every new line the current indentation is simply prefixed (if not at start of line, then it just emits as normal). Add a simple Region helper that makes it easy to have the C++ scope match the emitted scope. Use this in op doc generator and rewrite generator. This reverts revert commit be185b6 addresses shared lib failure by fixing up cmake files. Differential Revision: https://reviews.llvm.org/D84107
1 parent d20c602 commit 9b85152

File tree

8 files changed

+418
-166
lines changed

8 files changed

+418
-166
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//===- IndentedOstream.h - raw ostream wrapper to indent --------*- 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+
// raw_ostream subclass that keeps track of indentation for textual output
10+
// where indentation helps readability.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef MLIR_SUPPORT_INDENTEDOSTREAM_H_
15+
#define MLIR_SUPPORT_INDENTEDOSTREAM_H_
16+
17+
#include "mlir/Support/LLVM.h"
18+
#include "llvm/Support/raw_ostream.h"
19+
20+
namespace mlir {
21+
22+
/// raw_ostream subclass that simplifies indention a sequence of code.
23+
class raw_indented_ostream : public raw_ostream {
24+
public:
25+
explicit raw_indented_ostream(llvm::raw_ostream &os) : os(os) {
26+
SetUnbuffered();
27+
}
28+
29+
/// Simple RAII struct to use to indentation around entering/exiting region.
30+
struct DelimitedScope {
31+
explicit DelimitedScope(raw_indented_ostream &os, StringRef open = "",
32+
StringRef close = "")
33+
: os(os), open(open), close(close) {
34+
os << open;
35+
os.indent();
36+
}
37+
~DelimitedScope() {
38+
os.unindent();
39+
os << close;
40+
}
41+
42+
raw_indented_ostream &os;
43+
44+
private:
45+
llvm::StringRef open, close;
46+
};
47+
48+
/// Returns DelimitedScope.
49+
DelimitedScope scope(StringRef open = "", StringRef close = "") {
50+
return DelimitedScope(*this, open, close);
51+
}
52+
53+
/// Re-indents by removing the leading whitespace from the first non-empty
54+
/// line from every line of the the string, skipping over empty lines at the
55+
/// start.
56+
raw_indented_ostream &reindent(StringRef str);
57+
58+
/// Increases the indent and returning this raw_indented_ostream.
59+
raw_indented_ostream &indent() {
60+
currentIndent += indentSize;
61+
return *this;
62+
}
63+
64+
/// Decreases the indent and returning this raw_indented_ostream.
65+
raw_indented_ostream &unindent() {
66+
currentIndent = std::max(0, currentIndent - indentSize);
67+
return *this;
68+
}
69+
70+
/// Emits whitespace and sets the indendation for the stream.
71+
raw_indented_ostream &indent(int with) {
72+
os.indent(with);
73+
atStartOfLine = false;
74+
currentIndent = with;
75+
return *this;
76+
}
77+
78+
private:
79+
void write_impl(const char *ptr, size_t size) override;
80+
81+
/// Return the current position within the stream, not counting the bytes
82+
/// currently in the buffer.
83+
uint64_t current_pos() const override { return os.tell(); }
84+
85+
/// Constant indent added/removed.
86+
static constexpr int indentSize = 2;
87+
88+
// Tracker for current indentation.
89+
int currentIndent = 0;
90+
91+
// The leading whitespace of the string being printed, if reindent is used.
92+
int leadingWs = 0;
93+
94+
// Tracks whether at start of line and so indent is required or not.
95+
bool atStartOfLine = true;
96+
97+
// The underlying raw_ostream.
98+
raw_ostream &os;
99+
};
100+
101+
} // namespace mlir
102+
#endif // MLIR_SUPPORT_INDENTEDOSTREAM_H_

mlir/lib/Support/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
set(LLVM_OPTIONAL_SOURCES
22
FileUtilities.cpp
3+
IndentedOstream.cpp
34
MlirOptMain.cpp
45
StorageUniquer.cpp
56
ToolUtilities.cpp
@@ -27,3 +28,12 @@ add_mlir_library(MLIROptLib
2728
MLIRParser
2829
MLIRSupport
2930
)
31+
32+
# This doesn't use add_mlir_library as it is used in mlir-tblgen and else
33+
# mlir-tblgen ends up depending on mlir-generic-headers.
34+
add_llvm_library(MLIRSupportIndentedOstream
35+
IndentedOstream.cpp
36+
37+
LINK_LIBS PUBLIC
38+
LLVMSupport
39+
)

mlir/lib/Support/IndentedOstream.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===- IndentedOstream.cpp - raw ostream wrapper to indent ----------------===//
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+
// raw_ostream subclass that keeps track of indentation for textual output
10+
// where indentation helps readability.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "mlir/Support/IndentedOstream.h"
15+
16+
using namespace mlir;
17+
18+
raw_indented_ostream &mlir::raw_indented_ostream::reindent(StringRef str) {
19+
StringRef remaining = str;
20+
// Find leading whitespace indent.
21+
while (!remaining.empty()) {
22+
auto split = remaining.split('\n');
23+
size_t indent = split.first.find_first_not_of(" \t");
24+
if (indent != StringRef::npos) {
25+
leadingWs = indent;
26+
break;
27+
}
28+
remaining = split.second;
29+
}
30+
// Print, skipping the empty lines.
31+
*this << remaining;
32+
leadingWs = 0;
33+
return *this;
34+
}
35+
36+
void mlir::raw_indented_ostream::write_impl(const char *ptr, size_t size) {
37+
StringRef str(ptr, size);
38+
// Print out indented.
39+
auto print = [this](StringRef str) {
40+
if (atStartOfLine)
41+
os.indent(currentIndent) << str.substr(leadingWs);
42+
else
43+
os << str.substr(leadingWs);
44+
};
45+
46+
while (!str.empty()) {
47+
size_t idx = str.find('\n');
48+
if (idx == StringRef::npos) {
49+
if (!str.substr(leadingWs).empty()) {
50+
print(str);
51+
atStartOfLine = false;
52+
}
53+
break;
54+
}
55+
56+
auto split =
57+
std::make_pair(str.slice(0, idx), str.slice(idx + 1, StringRef::npos));
58+
// Print empty new line without spaces if line only has spaces.
59+
if (!split.first.ltrim().empty())
60+
print(split.first);
61+
os << '\n';
62+
atStartOfLine = true;
63+
str = split.second;
64+
}
65+
}

mlir/tools/mlir-tblgen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ add_tablegen(mlir-tblgen MLIR
2525
set_target_properties(mlir-tblgen PROPERTIES FOLDER "Tablegenning")
2626
target_link_libraries(mlir-tblgen
2727
PRIVATE
28+
MLIRSupportIndentedOstream
2829
MLIRTableGen)
2930

3031
mlir_check_all_link_libraries(mlir-tblgen)

mlir/tools/mlir-tblgen/OpDocGen.cpp

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "DocGenUtilities.h"
15+
#include "mlir/Support/IndentedOstream.h"
1516
#include "mlir/TableGen/GenInfo.h"
1617
#include "mlir/TableGen/Operator.h"
1718
#include "llvm/ADT/DenseMap.h"
@@ -35,39 +36,8 @@ using mlir::tblgen::Operator;
3536
// in a way the user wanted but has some additional indenting due to being
3637
// nested in the op definition.
3738
void mlir::tblgen::emitDescription(StringRef description, raw_ostream &os) {
38-
// Determine the minimum number of spaces in a line.
39-
size_t min_indent = -1;
40-
StringRef remaining = description;
41-
while (!remaining.empty()) {
42-
auto split = remaining.split('\n');
43-
size_t indent = split.first.find_first_not_of(" \t");
44-
if (indent != StringRef::npos)
45-
min_indent = std::min(indent, min_indent);
46-
remaining = split.second;
47-
}
48-
49-
// Print out the description indented.
50-
os << "\n";
51-
remaining = description;
52-
bool printed = false;
53-
while (!remaining.empty()) {
54-
auto split = remaining.split('\n');
55-
if (split.second.empty()) {
56-
// Skip last line with just spaces.
57-
if (split.first.ltrim().empty())
58-
break;
59-
}
60-
// Print empty new line without spaces if line only has spaces, unless no
61-
// text has been emitted before.
62-
if (split.first.ltrim().empty()) {
63-
if (printed)
64-
os << "\n";
65-
} else {
66-
os << split.first.substr(min_indent) << "\n";
67-
printed = true;
68-
}
69-
remaining = split.second;
70-
}
39+
raw_indented_ostream ros(os);
40+
ros.reindent(description.rtrim(" \t"));
7141
}
7242

7343
// Emits `str` with trailing newline if not empty.
@@ -116,7 +86,7 @@ static void emitOpDoc(Operator op, raw_ostream &os) {
11686

11787
// Emit the summary, syntax, and description if present.
11888
if (op.hasSummary())
119-
os << "\n" << op.getSummary() << "\n";
89+
os << "\n" << op.getSummary() << "\n\n";
12090
if (op.hasAssemblyFormat())
12191
emitAssemblyFormat(op.getOperationName(), op.getAssemblyFormat().trim(),
12292
os);
@@ -228,7 +198,7 @@ static void emitDialectDoc(const RecordKeeper &recordKeeper, raw_ostream &os) {
228198
}
229199

230200
os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
231-
for (auto dialectWithOps : dialectOps)
201+
for (const auto &dialectWithOps : dialectOps)
232202
emitDialectDoc(dialectWithOps.first, dialectWithOps.second,
233203
dialectTypes[dialectWithOps.first], os);
234204
}

0 commit comments

Comments
 (0)