Skip to content

Commit 8e9bdeb

Browse files
committed
[TableGen] Add !select operator to select records
This operator is like `!select<T>(regex)`, which produces a list of records whose type is `T` and record names match regular expression `regex`.
1 parent 4fb31e4 commit 8e9bdeb

File tree

7 files changed

+204
-5
lines changed

7 files changed

+204
-5
lines changed

llvm/docs/TableGen/ProgRef.rst

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,10 @@ TableGen provides "bang operators" that have a wide variety of uses:
226226
: !initialized !interleave !isa !le !listconcat
227227
: !listflatten !listremove !listsplat !logtwo !lt
228228
: !mul !ne !not !or !range
229-
: !repr !setdagarg !setdagname !setdagop !shl
230-
: !size !sra !srl !strconcat !sub
231-
: !subst !substr !tail !tolower !toupper
232-
: !xor
229+
: !repr !select !setdagarg !setdagname !setdagop
230+
: !shl !size !sra !srl !strconcat
231+
: !sub !subst !substr !tail !tolower
232+
: !toupper !xor
233233

234234
The ``!cond`` operator has a slightly different
235235
syntax compared to other bang operators, so it is defined separately:
@@ -1920,6 +1920,10 @@ and non-0 as true.
19201920
Represents *value* as a string. String format for the value is not
19211921
guaranteed to be stable. Intended for debugging purposes only.
19221922

1923+
``!select<``\ *type*\ ``>(``\ *regex*\ ``)``
1924+
This operator produces a list of records whose type is *type* and record
1925+
names match regular expression *regex*.
1926+
19231927
``!setdagarg(``\ *dag*\ ``,``\ *key*\ ``,``\ *arg*\ ``)``
19241928
This operator produces a DAG node with the same operator and arguments as
19251929
*dag*, but replacing the value of the argument specified by the *key* with

llvm/include/llvm/TableGen/Record.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ class Init {
316316
IK_FoldOpInit,
317317
IK_IsAOpInit,
318318
IK_ExistsOpInit,
319+
IK_SelectOpInit,
319320
IK_AnonymousNameInit,
320321
IK_StringInit,
321322
IK_VarInit,
@@ -1191,6 +1192,38 @@ class ExistsOpInit final : public TypedInit, public FoldingSetNode {
11911192
std::string getAsString() const override;
11921193
};
11931194

1195+
/// !select<type>(regex) - Produces a list of records whose type is `type` and
1196+
/// record names match regular expression `regex`.
1197+
class SelectOpInit final : public TypedInit, public FoldingSetNode {
1198+
private:
1199+
const RecTy *Type;
1200+
const Init *Regex;
1201+
1202+
SelectOpInit(const RecTy *Type, const Init *Regex)
1203+
: TypedInit(IK_SelectOpInit, IntRecTy::get(Type->getRecordKeeper())),
1204+
Type(Type), Regex(Regex) {}
1205+
1206+
public:
1207+
SelectOpInit(const SelectOpInit &) = delete;
1208+
SelectOpInit &operator=(const SelectOpInit &) = delete;
1209+
1210+
static bool classof(const Init *I) { return I->getKind() == IK_SelectOpInit; }
1211+
1212+
static const SelectOpInit *get(const RecTy *Type, const Init *Regex);
1213+
1214+
void Profile(FoldingSetNodeID &ID) const;
1215+
1216+
const Init *Fold() const;
1217+
1218+
bool isComplete() const override { return false; }
1219+
1220+
const Init *resolveReferences(Resolver &R) const override;
1221+
1222+
const Init *getBit(unsigned Bit) const override;
1223+
1224+
std::string getAsString() const override;
1225+
};
1226+
11941227
/// 'Opcode' - Represent a reference to an entire variable object.
11951228
class VarInit final : public TypedInit {
11961229
const Init *VarName;

llvm/lib/TableGen/Record.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "llvm/Support/Compiler.h"
2626
#include "llvm/Support/ErrorHandling.h"
2727
#include "llvm/Support/MathExtras.h"
28+
#include "llvm/Support/Regex.h"
2829
#include "llvm/Support/SMLoc.h"
2930
#include "llvm/Support/raw_ostream.h"
3031
#include "llvm/TableGen/Error.h"
@@ -83,6 +84,7 @@ struct RecordKeeperImpl {
8384
FoldingSet<FoldOpInit> TheFoldOpInitPool;
8485
FoldingSet<IsAOpInit> TheIsAOpInitPool;
8586
FoldingSet<ExistsOpInit> TheExistsOpInitPool;
87+
FoldingSet<SelectOpInit> TheSelectOpInitPool;
8688
DenseMap<std::pair<const RecTy *, const Init *>, VarInit *> TheVarInitPool;
8789
DenseMap<std::pair<const TypedInit *, unsigned>, VarBitInit *>
8890
TheVarBitInitPool;
@@ -2199,6 +2201,66 @@ std::string ExistsOpInit::getAsString() const {
21992201
.str();
22002202
}
22012203

2204+
static void ProfileSelectOpInit(FoldingSetNodeID &ID, const RecTy *Type,
2205+
const Init *Regex) {
2206+
ID.AddPointer(Type);
2207+
ID.AddPointer(Regex);
2208+
}
2209+
2210+
const SelectOpInit *SelectOpInit::get(const RecTy *Type, const Init *Regex) {
2211+
FoldingSetNodeID ID;
2212+
ProfileSelectOpInit(ID, Type, Regex);
2213+
2214+
detail::RecordKeeperImpl &RK = Regex->getRecordKeeper().getImpl();
2215+
void *IP = nullptr;
2216+
if (const SelectOpInit *I =
2217+
RK.TheSelectOpInitPool.FindNodeOrInsertPos(ID, IP))
2218+
return I;
2219+
2220+
SelectOpInit *I = new (RK.Allocator) SelectOpInit(Type, Regex);
2221+
RK.TheSelectOpInitPool.InsertNode(I, IP);
2222+
return I;
2223+
}
2224+
2225+
void SelectOpInit::Profile(FoldingSetNodeID &ID) const {
2226+
ProfileSelectOpInit(ID, Type, Regex);
2227+
}
2228+
2229+
const Init *SelectOpInit::Fold() const {
2230+
if (const auto *RegexInit = dyn_cast<StringInit>(Regex)) {
2231+
StringRef RegexStr = RegexInit->getValue();
2232+
llvm::Regex Matcher(RegexStr);
2233+
if (!Matcher.isValid())
2234+
PrintFatalError(Twine("invalid regex '") + RegexStr + Twine("'"));
2235+
2236+
const RecordKeeper &RK = Type->getRecordKeeper();
2237+
SmallVector<Init *, 8> Selected;
2238+
for (auto &Def : RK.getAllDerivedDefinitionsIfDefined(Type->getAsString()))
2239+
if (Matcher.match(Def->getName()))
2240+
Selected.push_back(Def->getDefInit());
2241+
2242+
return ListInit::get(Selected, Type);
2243+
}
2244+
return this;
2245+
}
2246+
2247+
const Init *SelectOpInit::resolveReferences(Resolver &R) const {
2248+
const Init *NewRegex = Regex->resolveReferences(R);
2249+
if (Regex != NewRegex)
2250+
return get(Type, NewRegex)->Fold();
2251+
return this;
2252+
}
2253+
2254+
const Init *SelectOpInit::getBit(unsigned Bit) const {
2255+
return VarBitInit::get(this, Bit);
2256+
}
2257+
2258+
std::string SelectOpInit::getAsString() const {
2259+
return (Twine("!select<") + Type->getAsString() + ">(" +
2260+
Regex->getAsString() + ")")
2261+
.str();
2262+
}
2263+
22022264
const RecTy *TypedInit::getFieldType(const StringInit *FieldName) const {
22032265
if (const auto *RecordType = dyn_cast<RecordRecTy>(getType())) {
22042266
for (const Record *Rec : RecordType->getClasses()) {

llvm/lib/TableGen/TGLexer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ tgtok::TokKind TGLexer::LexExclaim() {
644644
.Case("tolower", tgtok::XToLower)
645645
.Case("toupper", tgtok::XToUpper)
646646
.Case("repr", tgtok::XRepr)
647+
.Case("select", tgtok::XSelect)
647648
.Default(tgtok::Error);
648649

649650
return Kind != tgtok::Error ? Kind

llvm/lib/TableGen/TGLexer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ enum TokKind {
158158
XSetDagArg,
159159
XSetDagName,
160160
XRepr,
161-
BANG_OPERATOR_LAST = XRepr,
161+
XSelect,
162+
BANG_OPERATOR_LAST = XSelect,
162163

163164
// String valued tokens.
164165
STRING_VALUE_FIRST,

llvm/lib/TableGen/TGParser.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,6 +1455,44 @@ const Init *TGParser::ParseOperation(Record *CurRec, const RecTy *ItemType) {
14551455
return (ExistsOpInit::get(Type, Expr))->Fold(CurRec);
14561456
}
14571457

1458+
case tgtok::XSelect: {
1459+
// Value ::= !select '<' Type '>' '(' Regex ')'
1460+
Lex.Lex(); // eat the operation.
1461+
1462+
const RecTy *Type = ParseOperatorType();
1463+
if (!Type)
1464+
return nullptr;
1465+
1466+
if (!consume(tgtok::l_paren)) {
1467+
TokError("expected '(' after type of !select");
1468+
return nullptr;
1469+
}
1470+
1471+
SMLoc RegexLoc = Lex.getLoc();
1472+
const Init *Regex = ParseValue(CurRec);
1473+
if (!Regex)
1474+
return nullptr;
1475+
1476+
const auto *RegexType = dyn_cast<TypedInit>(Regex);
1477+
if (!RegexType) {
1478+
Error(RegexLoc, "expected string type argument in !select operator");
1479+
return nullptr;
1480+
}
1481+
1482+
const auto *SType = dyn_cast<StringRecTy>(RegexType->getType());
1483+
if (!SType) {
1484+
Error(RegexLoc, "expected string type argument in !select operator");
1485+
return nullptr;
1486+
}
1487+
1488+
if (!consume(tgtok::r_paren)) {
1489+
TokError("expected ')' in !select");
1490+
return nullptr;
1491+
}
1492+
1493+
return (SelectOpInit::get(Type, Regex))->Fold();
1494+
}
1495+
14581496
case tgtok::XConcat:
14591497
case tgtok::XADD:
14601498
case tgtok::XSUB:

llvm/test/TableGen/select.td

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: llvm-tblgen %s | FileCheck %s
2+
// RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s
3+
// RUN: not llvm-tblgen -DERROR2 %s 2>&1 | FileCheck --check-prefix=ERROR2 %s
4+
// RUN: not llvm-tblgen -DERROR3 %s 2>&1 | FileCheck --check-prefix=ERROR3 %s
5+
// XFAIL: vg_leak
6+
7+
class A;
8+
def a0 : A;
9+
def a1 : A;
10+
11+
class B : A;
12+
def b0 : B;
13+
def b1 : B;
14+
15+
def select_A {
16+
list<A> selected = !select<A>(".*");
17+
}
18+
19+
def select_A_x0 {
20+
list<A> selected = !select<A>(".*0");
21+
}
22+
23+
def select_A_x1 {
24+
list<A> selected = !select<A>(".*1");
25+
}
26+
27+
def select_B {
28+
list<B> selected = !select<B>(".*");
29+
}
30+
31+
// CHECK-LABEL: def select_A {
32+
// CHECK-NEXT: list<A> selected = [a0, a1, b0, b1];
33+
// CHECK-NEXT: }
34+
35+
// CHECK-LABEL: def select_A_x0 {
36+
// CHECK-NEXT: list<A> selected = [a0, b0];
37+
// CHECK-NEXT: }
38+
39+
// CHECK-LABEL: def select_A_x1 {
40+
// CHECK-NEXT: list<A> selected = [a1, b1];
41+
// CHECK-NEXT: }
42+
43+
// CHECK-LABEL: def select_B {
44+
// CHECK-NEXT: list<B> selected = [b0, b1];
45+
// CHECK-NEXT: }
46+
47+
#ifdef ERROR1
48+
defvar error1 = !select<A>(123)
49+
// ERROR1: error: expected string type argument in !select operator
50+
#endif
51+
52+
#ifdef ERROR2
53+
defvar error2 = !select<1>("")
54+
// ERROR2: error: Unknown token when expecting a type
55+
#endif
56+
57+
#ifdef ERROR3
58+
defvar error3 = !select<A>("([)]")
59+
// ERROR3: error: invalid regex '([)]'
60+
#endif

0 commit comments

Comments
 (0)