Skip to content

Commit eb27b5d

Browse files
committedOct 3, 2019
[gicombiner] Add a CodeExpander to handle C++ fragments with variable expansion
Summary: This will handle expansion of C++ fragments in the declarative combiner including custom predicates, and escapes into C++ to aid the migration effort. Reviewers: bogner, volkan Subscribers: mgorny, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D68288 llvm-svn: 373551
1 parent 81f2da4 commit eb27b5d

9 files changed

+422
-1
lines changed
 

‎llvm/unittests/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ add_subdirectory(Remarks)
3131
add_subdirectory(Passes)
3232
add_subdirectory(ProfileData)
3333
add_subdirectory(Support)
34-
add_subdirectory(TextAPI)
34+
add_subdirectory(TableGen)
3535
add_subdirectory(Target)
36+
add_subdirectory(TextAPI)
3637
add_subdirectory(Transforms)
3738
add_subdirectory(XRay)
3839
add_subdirectory(tools)
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
set(LLVM_LINK_COMPONENTS
2+
TableGen
3+
Support
4+
)
5+
6+
add_llvm_unittest(TableGenTests
7+
CodeExpanderTest.cpp
8+
$<TARGET_OBJECTS:obj.LLVMTableGenGlobalISel>
9+
)
10+
11+
include_directories(${CMAKE_SOURCE_DIR}/utils/TableGen)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
//===- llvm/unittest/TableGen/CodeExpanderTest.cpp - Tests ----------------===//
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+
#include "GlobalISel/CodeExpander.h"
10+
#include "GlobalISel/CodeExpansions.h"
11+
12+
#include "llvm/Support/raw_ostream.h"
13+
#include "llvm/TableGen/Error.h"
14+
#include "gtest/gtest.h"
15+
16+
using namespace llvm;
17+
18+
static StringRef bufferize(StringRef Str) {
19+
std::unique_ptr<MemoryBuffer> Buffer =
20+
MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
21+
StringRef StrBufferRef = Buffer->getBuffer();
22+
SrcMgr.AddNewSourceBuffer(std::move(Buffer), SMLoc());
23+
return StrBufferRef;
24+
}
25+
26+
class RAIIDiagnosticChecker {
27+
std::string EmittedDiags;
28+
raw_string_ostream OS;
29+
std::vector<SMDiagnostic> Expected;
30+
std::vector<SMDiagnostic> Received;
31+
32+
public:
33+
RAIIDiagnosticChecker() : OS(EmittedDiags) {
34+
SrcMgr.setDiagHandler(handler, this);
35+
}
36+
~RAIIDiagnosticChecker() {
37+
SrcMgr.setDiagHandler(nullptr);
38+
EXPECT_EQ(Received.size(), Expected.size());
39+
for (unsigned i = 0; i < Received.size() && i < Expected.size(); ++i) {
40+
EXPECT_EQ(Received[i].getLoc(), Expected[i].getLoc());
41+
EXPECT_EQ(Received[i].getFilename(), Expected[i].getFilename());
42+
EXPECT_EQ(Received[i].getKind(), Expected[i].getKind());
43+
EXPECT_EQ(Received[i].getLineNo(), Expected[i].getLineNo());
44+
EXPECT_EQ(Received[i].getColumnNo(), Expected[i].getColumnNo());
45+
EXPECT_EQ(Received[i].getMessage(), Expected[i].getMessage());
46+
EXPECT_EQ(Received[i].getLineContents(), Expected[i].getLineContents());
47+
EXPECT_EQ(Received[i].getRanges(), Expected[i].getRanges());
48+
}
49+
50+
if (testing::Test::HasFailure())
51+
errs() << "Emitted diagnostic:\n" << OS.str();
52+
}
53+
54+
void expect(SMDiagnostic D) { Expected.push_back(D); }
55+
56+
void diag(const SMDiagnostic &D) {
57+
Received.push_back(D);
58+
}
59+
60+
static void handler(const SMDiagnostic &D, void *Context) {
61+
RAIIDiagnosticChecker *Self = static_cast<RAIIDiagnosticChecker *>(Context);
62+
Self->diag(D);
63+
SrcMgr.setDiagHandler(nullptr);
64+
SrcMgr.PrintMessage(Self->OS, D);
65+
SrcMgr.setDiagHandler(handler, Context);
66+
};
67+
};
68+
69+
TEST(CodeExpander, NoExpansions) {
70+
std::string Result;
71+
raw_string_ostream OS(Result);
72+
CodeExpansions Expansions;
73+
74+
RAIIDiagnosticChecker DiagChecker;
75+
CodeExpander("No expansions", Expansions, SMLoc(), false).emit(OS);
76+
EXPECT_EQ(OS.str(), "No expansions");
77+
}
78+
79+
// Indentation is applied to all lines except the first
80+
TEST(CodeExpander, Indentation) {
81+
std::string Result;
82+
raw_string_ostream OS(Result);
83+
CodeExpansions Expansions;
84+
85+
RAIIDiagnosticChecker DiagChecker;
86+
CodeExpander("No expansions\nsecond line\nthird line", Expansions, SMLoc(),
87+
false, " ")
88+
.emit(OS);
89+
EXPECT_EQ(OS.str(), "No expansions\n second line\n third line");
90+
}
91+
92+
// \ is an escape character that removes special meanings from the next
93+
// character.
94+
TEST(CodeExpander, Escape) {
95+
std::string Result;
96+
raw_string_ostream OS(Result);
97+
CodeExpansions Expansions;
98+
99+
RAIIDiagnosticChecker DiagChecker;
100+
CodeExpander("\\\\\\a\\$", Expansions, SMLoc(), false).emit(OS);
101+
EXPECT_EQ(OS.str(), "\\a$");
102+
}
103+
104+
// $foo is not an expansion. It should warn though.
105+
TEST(CodeExpander, NotAnExpansion) {
106+
std::string Result;
107+
raw_string_ostream OS(Result);
108+
CodeExpansions Expansions;
109+
110+
RAIIDiagnosticChecker DiagChecker;
111+
StringRef In = bufferize(" $foo");
112+
CodeExpander(" $foo", Expansions, SMLoc::getFromPointer(In.data()), false)
113+
.emit(OS);
114+
EXPECT_EQ(OS.str(), " $foo");
115+
DiagChecker.expect(SMDiagnostic(
116+
SrcMgr, SMLoc::getFromPointer(In.data() + 1), "TestBuffer", 1, 1,
117+
SourceMgr::DK_Warning, "Assuming missing escape character", " $foo", {}));
118+
}
119+
120+
// \$foo is not an expansion but shouldn't warn as it's using the escape.
121+
TEST(CodeExpander, EscapedNotAnExpansion) {
122+
std::string Result;
123+
raw_string_ostream OS(Result);
124+
CodeExpansions Expansions;
125+
126+
RAIIDiagnosticChecker DiagChecker;
127+
CodeExpander("\\$foo", Expansions, SMLoc(), false).emit(OS);
128+
EXPECT_EQ(OS.str(), "$foo");
129+
}
130+
131+
// \${foo is not an expansion but shouldn't warn as it's using the escape.
132+
TEST(CodeExpander, EscapedUnterminatedExpansion) {
133+
std::string Result;
134+
raw_string_ostream OS(Result);
135+
CodeExpansions Expansions;
136+
137+
RAIIDiagnosticChecker DiagChecker;
138+
CodeExpander("\\${foo", Expansions, SMLoc(), false).emit(OS);
139+
EXPECT_EQ(OS.str(), "${foo");
140+
}
141+
142+
// \${foo is not an expansion but shouldn't warn as it's using the escape.
143+
TEST(CodeExpander, EscapedExpansion) {
144+
std::string Result;
145+
raw_string_ostream OS(Result);
146+
CodeExpansions Expansions;
147+
148+
RAIIDiagnosticChecker DiagChecker;
149+
CodeExpander("\\${foo}", Expansions, SMLoc(), false).emit(OS);
150+
EXPECT_EQ(OS.str(), "${foo}");
151+
}
152+
153+
// ${foo} is an undefined expansion and should error.
154+
TEST(CodeExpander, UndefinedExpansion) {
155+
std::string Result;
156+
raw_string_ostream OS(Result);
157+
CodeExpansions Expansions;
158+
Expansions.declare("bar", "expansion");
159+
160+
RAIIDiagnosticChecker DiagChecker;
161+
CodeExpander("${foo}${bar}", Expansions, SMLoc(), false).emit(OS);
162+
EXPECT_EQ(OS.str(), "expansion");
163+
DiagChecker.expect(
164+
SMDiagnostic(SrcMgr, SMLoc(), "<unknown>", 0, -1, SourceMgr::DK_Error,
165+
"Attempting to expand an undeclared variable foo", "", {}));
166+
}
167+
168+
// ${foo} is an undefined expansion and should error. When given a valid
169+
// location for the start of the buffer it should correctly point at the
170+
// expansion being performed.
171+
TEST(CodeExpander, UndefinedExpansionWithLoc) {
172+
std::string Result;
173+
raw_string_ostream OS(Result);
174+
CodeExpansions Expansions;
175+
Expansions.declare("bar", "expansion");
176+
177+
RAIIDiagnosticChecker DiagChecker;
178+
StringRef In = bufferize("Padding ${foo}${bar}");
179+
CodeExpander(In, Expansions, SMLoc::getFromPointer(In.data()), false)
180+
.emit(OS);
181+
EXPECT_EQ(OS.str(), "Padding expansion");
182+
DiagChecker.expect(SMDiagnostic(
183+
SrcMgr, SMLoc::getFromPointer(In.data() + 8), "TestBuffer", 1, 8,
184+
SourceMgr::DK_Error, "Attempting to expand an undeclared variable foo",
185+
"Padding ${foo}${bar}", {}));
186+
}
187+
188+
// ${bar is an unterminated expansion. Warn and implicitly terminate it.
189+
TEST(CodeExpander, UnterminatedExpansion) {
190+
std::string Result;
191+
raw_string_ostream OS(Result);
192+
CodeExpansions Expansions;
193+
Expansions.declare("bar", "expansion");
194+
195+
RAIIDiagnosticChecker DiagChecker;
196+
StringRef In = bufferize(" ${bar");
197+
CodeExpander(In, Expansions, SMLoc::getFromPointer(In.data()), false)
198+
.emit(OS);
199+
EXPECT_EQ(OS.str(), " expansion");
200+
DiagChecker.expect(SMDiagnostic(SrcMgr, SMLoc::getFromPointer(In.data() + 1),
201+
"TestBuffer", 1, 1, SourceMgr::DK_Warning,
202+
"Unterminated expansion", " ${bar", {}));
203+
}

‎llvm/utils/TableGen/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
add_subdirectory(GlobalISel)
2+
13
set(LLVM_LINK_COMPONENTS Support)
24

35
add_tablegen(llvm-tblgen LLVM
@@ -49,5 +51,6 @@ add_tablegen(llvm-tblgen LLVM
4951
X86RecognizableInstr.cpp
5052
WebAssemblyDisassemblerEmitter.cpp
5153
CTagsEmitter.cpp
54+
$<TARGET_OBJECTS:obj.LLVMTableGenGlobalISel>
5255
)
5356
set_target_properties(llvm-tblgen PROPERTIES FOLDER "Tablegenning")

‎llvm/utils/TableGen/GICombinerEmitter.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ cl::OptionCategory
2626
static cl::list<std::string>
2727
SelectedCombiners("combiners", cl::desc("Emit the specified combiners"),
2828
cl::cat(GICombinerEmitterCat), cl::CommaSeparated);
29+
static cl::opt<bool> ShowExpansions(
30+
"gicombiner-show-expansions",
31+
cl::desc("Use C++ comments to indicate occurence of code expansion"),
32+
cl::cat(GICombinerEmitterCat));
33+
2934
namespace {
3035
class GICombinerEmitter {
3136
StringRef Name;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
set(LLVM_LINK_COMPONENTS
2+
Support
3+
)
4+
5+
llvm_add_library(LLVMTableGenGlobalISel OBJECT
6+
CodeExpander.cpp
7+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//===- CodeExpander.cpp - Expand variables in a string --------------------===//
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+
/// \file Expand the variables in a string.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "CodeExpander.h"
14+
#include "CodeExpansions.h"
15+
#include "llvm/Support/CommandLine.h"
16+
#include "llvm/Support/raw_ostream.h"
17+
#include "llvm/TableGen/Error.h"
18+
19+
using namespace llvm;
20+
21+
void CodeExpander::emit(raw_ostream &OS) const {
22+
StringRef Current = Code;
23+
24+
while (!Current.empty()) {
25+
size_t Pos = Current.find_first_of("$\n\\");
26+
if (Pos == StringRef::npos) {
27+
OS << Current;
28+
Current = "";
29+
continue;
30+
}
31+
32+
OS << Current.substr(0, Pos);
33+
Current = Current.substr(Pos);
34+
35+
if (Current.startswith("\n")) {
36+
OS << "\n" << Indent;
37+
Current = Current.drop_front(1);
38+
continue;
39+
}
40+
41+
if (Current.startswith("\\$") || Current.startswith("\\\\")) {
42+
OS << Current[1];
43+
Current = Current.drop_front(2);
44+
continue;
45+
}
46+
47+
if (Current.startswith("\\")) {
48+
Current = Current.drop_front(1);
49+
continue;
50+
}
51+
52+
if (Current.startswith("${")) {
53+
StringRef StartVar = Current;
54+
Current = Current.drop_front(2);
55+
StringRef Var;
56+
std::tie(Var, Current) = Current.split("}");
57+
58+
// Warn if we split because no terminator was found.
59+
StringRef EndVar = StartVar.drop_front(2 /* ${ */ + Var.size());
60+
if (EndVar.empty()) {
61+
size_t LocOffset = StartVar.data() - Code.data();
62+
PrintWarning(
63+
Loc.size() > 0 && Loc[0].isValid()
64+
? SMLoc::getFromPointer(Loc[0].getPointer() + LocOffset)
65+
: SMLoc(),
66+
"Unterminated expansion");
67+
}
68+
69+
auto ValueI = Expansions.find(Var);
70+
if (ValueI == Expansions.end()) {
71+
size_t LocOffset = StartVar.data() - Code.data();
72+
PrintError(Loc.size() > 0 && Loc[0].isValid()
73+
? SMLoc::getFromPointer(Loc[0].getPointer() + LocOffset)
74+
: SMLoc(),
75+
"Attempting to expand an undeclared variable " + Var);
76+
}
77+
if (ShowExpansions)
78+
OS << "/*$" << Var << "{*/";
79+
OS << Expansions.lookup(Var);
80+
if (ShowExpansions)
81+
OS << "/*}*/";
82+
continue;
83+
}
84+
85+
size_t LocOffset = Current.data() - Code.data();
86+
PrintWarning(Loc.size() > 0 && Loc[0].isValid()
87+
? SMLoc::getFromPointer(Loc[0].getPointer() + LocOffset)
88+
: SMLoc(),
89+
"Assuming missing escape character");
90+
OS << "$";
91+
Current = Current.drop_front(1);
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//===- CodeExpander.h - Expand variables in a string ----------------------===//
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+
/// \file Expand the variables in a string.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_UTILS_TABLEGEN_CODEEXPANDER_H
14+
#define LLVM_UTILS_TABLEGEN_CODEEXPANDER_H
15+
16+
#include "llvm/ADT/ArrayRef.h"
17+
#include "llvm/ADT/StringRef.h"
18+
#include "llvm/Support/SMLoc.h"
19+
20+
namespace llvm {
21+
class CodeExpansions;
22+
class raw_ostream;
23+
24+
/// Emit the given code with all '${foo}' placeholders expanded to their
25+
/// replacements.
26+
///
27+
/// It's an error to use an undefined expansion and expansion-like output that
28+
/// needs to be emitted verbatim can be escaped as '\${foo}'
29+
///
30+
/// The emitted code can be given a custom indent to enable both indentation by
31+
/// an arbitrary amount of whitespace and emission of the code as a comment.
32+
class CodeExpander {
33+
StringRef Code;
34+
const CodeExpansions &Expansions;
35+
const ArrayRef<SMLoc> &Loc;
36+
bool ShowExpansions;
37+
StringRef Indent;
38+
39+
public:
40+
CodeExpander(StringRef Code, const CodeExpansions &Expansions,
41+
const ArrayRef<SMLoc> &Loc, bool ShowExpansions,
42+
StringRef Indent = " ")
43+
: Code(Code), Expansions(Expansions), Loc(Loc),
44+
ShowExpansions(ShowExpansions), Indent(Indent) {}
45+
46+
void emit(raw_ostream &OS) const;
47+
};
48+
49+
inline raw_ostream &operator<<(raw_ostream &OS, const CodeExpander &Expander) {
50+
Expander.emit(OS);
51+
return OS;
52+
}
53+
} // end namespace llvm
54+
55+
#endif // ifndef LLVM_UTILS_TABLEGEN_CODEEXPANDER_H
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//===- CodeExpansions.h - Record expansions for CodeExpander --------------===//
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+
/// \file Record the expansions to use in a CodeExpander.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/ADT/StringMap.h"
14+
15+
#ifndef LLVM_UTILS_TABLEGEN_CODEEXPANSIONS_H
16+
#define LLVM_UTILS_TABLEGEN_CODEEXPANSIONS_H
17+
namespace llvm {
18+
class CodeExpansions {
19+
public:
20+
using const_iterator = StringMap<std::string>::const_iterator;
21+
22+
protected:
23+
StringMap<std::string> Expansions;
24+
25+
public:
26+
void declare(StringRef Name, StringRef Expansion) {
27+
bool Inserted = Expansions.try_emplace(Name, Expansion).second;
28+
assert(Inserted && "Declared variable twice");
29+
(void)Inserted;
30+
}
31+
32+
std::string lookup(StringRef Variable) const {
33+
return Expansions.lookup(Variable);
34+
}
35+
36+
const_iterator begin() const { return Expansions.begin(); }
37+
const_iterator end() const { return Expansions.end(); }
38+
const_iterator find(StringRef Variable) const {
39+
return Expansions.find(Variable);
40+
}
41+
};
42+
} // end namespace llvm
43+
#endif // ifndef LLVM_UTILS_TABLEGEN_CODEEXPANSIONS_H

0 commit comments

Comments
 (0)
Please sign in to comment.