Skip to content

Commit d1535bf

Browse files
committed
[TableGen] Add !records operator to get defined records
The format is: `!records<T>([regex])`. This operator produces a list of records whose type is `T`. If `regex` is provided, only records whose name matches the regular expression `regex` will be included. The format of `regex` is ERE (Extended POSIX Regular Expressions).
1 parent 17b4be8 commit d1535bf

File tree

7 files changed

+211
-4
lines changed

7 files changed

+211
-4
lines changed

llvm/docs/TableGen/ProgRef.rst

Lines changed: 10 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
: !match !mul !ne !not !or
229-
: !range !repr !setdagarg !setdagname !setdagop
230-
: !shl !size !sra !srl !strconcat
231-
: !sub !subst !substr !tail !tolower
232-
: !toupper !xor
229+
: !range !records !repr !setdagarg !setdagname
230+
: !setdagop !shl !size !sra !srl
231+
: !strconcat !sub !subst !substr !tail
232+
: !tolower !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,12 @@ and non-0 as true.
19201920
``!range(``\ *list*\ ``)``
19211921
Equivalent to ``!range(0, !size(list))``.
19221922

1923+
``!records<``\ *type*\ ``>([``\ *regex*\ ``])``
1924+
This operator produces a list of records whose type is *type*. If *regex*
1925+
is provided, only records whose name matches the regular expression *regex*
1926+
will be included. The format of *regex* is ERE (Extended POSIX Regular
1927+
Expressions).
1928+
19231929
``!repr(``\ *value*\ ``)``
19241930
Represents *value* as a string. String format for the value is not
19251931
guaranteed to be stable. Intended for debugging purposes only.

llvm/include/llvm/TableGen/Record.h

Lines changed: 36 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_RecordsOpInit,
319320
IK_AnonymousNameInit,
320321
IK_StringInit,
321322
IK_VarInit,
@@ -1192,6 +1193,41 @@ class ExistsOpInit final : public TypedInit, public FoldingSetNode {
11921193
std::string getAsString() const override;
11931194
};
11941195

1196+
/// !records<type>([regex]) - Produces a list of records whose type is `type`.
1197+
/// If `regex` is provided, only records whose name matches the regular
1198+
/// expression `regex` will be included.
1199+
class RecordsOpInit final : public TypedInit, public FoldingSetNode {
1200+
private:
1201+
const RecTy *Type;
1202+
const Init *Regex;
1203+
1204+
RecordsOpInit(const RecTy *Type, const Init *Regex)
1205+
: TypedInit(IK_RecordsOpInit, ListRecTy::get(Type)), Type(Type),
1206+
Regex(Regex) {}
1207+
1208+
public:
1209+
RecordsOpInit(const RecordsOpInit &) = delete;
1210+
RecordsOpInit &operator=(const RecordsOpInit &) = delete;
1211+
1212+
static bool classof(const Init *I) {
1213+
return I->getKind() == IK_RecordsOpInit;
1214+
}
1215+
1216+
static const RecordsOpInit *get(const RecTy *Type, const Init *Regex);
1217+
1218+
void Profile(FoldingSetNodeID &ID) const;
1219+
1220+
const Init *Fold() const;
1221+
1222+
bool isComplete() const override { return false; }
1223+
1224+
const Init *resolveReferences(Resolver &R) const override;
1225+
1226+
const Init *getBit(unsigned Bit) const override;
1227+
1228+
std::string getAsString() const override;
1229+
};
1230+
11951231
/// 'Opcode' - Represent a reference to an entire variable object.
11961232
class VarInit final : public TypedInit {
11971233
const Init *VarName;

llvm/lib/TableGen/Record.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ struct RecordKeeperImpl {
8484
FoldingSet<FoldOpInit> TheFoldOpInitPool;
8585
FoldingSet<IsAOpInit> TheIsAOpInitPool;
8686
FoldingSet<ExistsOpInit> TheExistsOpInitPool;
87+
FoldingSet<RecordsOpInit> TheRecordsOpInitPool;
8788
DenseMap<std::pair<const RecTy *, const Init *>, VarInit *> TheVarInitPool;
8889
DenseMap<std::pair<const TypedInit *, unsigned>, VarBitInit *>
8990
TheVarBitInitPool;
@@ -2220,6 +2221,65 @@ std::string ExistsOpInit::getAsString() const {
22202221
.str();
22212222
}
22222223

2224+
static void ProfileRecordsOpInit(FoldingSetNodeID &ID, const RecTy *Type,
2225+
const Init *Regex) {
2226+
ID.AddPointer(Type);
2227+
ID.AddPointer(Regex);
2228+
}
2229+
2230+
const RecordsOpInit *RecordsOpInit::get(const RecTy *Type, const Init *Regex) {
2231+
FoldingSetNodeID ID;
2232+
ProfileRecordsOpInit(ID, Type, Regex);
2233+
2234+
detail::RecordKeeperImpl &RK = Regex->getRecordKeeper().getImpl();
2235+
void *IP = nullptr;
2236+
if (const RecordsOpInit *I =
2237+
RK.TheRecordsOpInitPool.FindNodeOrInsertPos(ID, IP))
2238+
return I;
2239+
2240+
RecordsOpInit *I = new (RK.Allocator) RecordsOpInit(Type, Regex);
2241+
RK.TheRecordsOpInitPool.InsertNode(I, IP);
2242+
return I;
2243+
}
2244+
2245+
void RecordsOpInit::Profile(FoldingSetNodeID &ID) const {
2246+
ProfileRecordsOpInit(ID, Type, Regex);
2247+
}
2248+
2249+
const Init *RecordsOpInit::Fold() const {
2250+
const auto *RegexInit = dyn_cast<StringInit>(Regex);
2251+
if (!RegexInit)
2252+
return this;
2253+
2254+
StringRef RegexStr = RegexInit->getValue();
2255+
llvm::Regex Matcher(RegexStr);
2256+
if (!Matcher.isValid())
2257+
PrintFatalError(Twine("invalid regex '") + RegexStr + Twine("'"));
2258+
2259+
const RecordKeeper &RK = Type->getRecordKeeper();
2260+
SmallVector<Init *, 8> Selected;
2261+
for (auto &Def : RK.getAllDerivedDefinitionsIfDefined(Type->getAsString()))
2262+
if (Matcher.match(Def->getName()))
2263+
Selected.push_back(Def->getDefInit());
2264+
2265+
return ListInit::get(Selected, Type);
2266+
}
2267+
2268+
const Init *RecordsOpInit::resolveReferences(Resolver &R) const {
2269+
const Init *NewRegex = Regex->resolveReferences(R);
2270+
if (Regex != NewRegex)
2271+
return get(Type, NewRegex)->Fold();
2272+
return this;
2273+
}
2274+
2275+
const Init *RecordsOpInit::getBit(unsigned Bit) const {
2276+
return VarBitInit::get(this, Bit);
2277+
}
2278+
2279+
std::string RecordsOpInit::getAsString() const {
2280+
return "!records<" + Type->getAsString() + ">(" + Regex->getAsString() + ")";
2281+
}
2282+
22232283
const RecTy *TypedInit::getFieldType(const StringInit *FieldName) const {
22242284
if (const auto *RecordType = dyn_cast<RecordRecTy>(getType())) {
22252285
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
@@ -629,6 +629,7 @@ tgtok::TokKind TGLexer::LexExclaim() {
629629
.Case("listsplat", tgtok::XListSplat)
630630
.Case("listremove", tgtok::XListRemove)
631631
.Case("range", tgtok::XRange)
632+
.Case("records", tgtok::XRecords)
632633
.Case("strconcat", tgtok::XStrConcat)
633634
.Case("initialized", tgtok::XInitialized)
634635
.Case("interleave", tgtok::XInterleave)

llvm/lib/TableGen/TGLexer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ enum TokKind {
154154
XToLower,
155155
XToUpper,
156156
XRange,
157+
XRecords,
157158
XGetDagArg,
158159
XGetDagName,
159160
XSetDagArg,

llvm/lib/TableGen/TGParser.cpp

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

1458+
case tgtok::XRecords: {
1459+
// Value ::= !records '<' 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 !records");
1468+
return nullptr;
1469+
}
1470+
1471+
// The Regex can be optional.
1472+
const Init *Regex;
1473+
if (Lex.getCode() != tgtok::r_paren) {
1474+
SMLoc RegexLoc = Lex.getLoc();
1475+
Regex = ParseValue(CurRec);
1476+
1477+
const auto *RegexType = dyn_cast<TypedInit>(Regex);
1478+
if (!RegexType) {
1479+
Error(RegexLoc, "expected string type argument in !records operator");
1480+
return nullptr;
1481+
}
1482+
1483+
const auto *SType = dyn_cast<StringRecTy>(RegexType->getType());
1484+
if (!SType) {
1485+
Error(RegexLoc, "expected string type argument in !records operator");
1486+
return nullptr;
1487+
}
1488+
} else {
1489+
// Use wildcard when Regex is not specified.
1490+
Regex = StringInit::get(Records, ".*");
1491+
}
1492+
1493+
if (!consume(tgtok::r_paren)) {
1494+
TokError("expected ')' in !records");
1495+
return nullptr;
1496+
}
1497+
1498+
return RecordsOpInit::get(Type, Regex)->Fold();
1499+
}
1500+
14581501
case tgtok::XConcat:
14591502
case tgtok::XMatch:
14601503
case tgtok::XADD:

llvm/test/TableGen/records.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 records_A {
16+
list<A> records = !records<A>();
17+
}
18+
19+
def records_A_x0 {
20+
list<A> records = !records<A>(".*0");
21+
}
22+
23+
def records_A_x1 {
24+
list<A> records = !records<A>(".*1");
25+
}
26+
27+
def records_B {
28+
list<B> records = !records<B>();
29+
}
30+
31+
// CHECK-LABEL: def records_A {
32+
// CHECK-NEXT: list<A> records = [a0, a1, b0, b1];
33+
// CHECK-NEXT: }
34+
35+
// CHECK-LABEL: def records_A_x0 {
36+
// CHECK-NEXT: list<A> records = [a0, b0];
37+
// CHECK-NEXT: }
38+
39+
// CHECK-LABEL: def records_A_x1 {
40+
// CHECK-NEXT: list<A> records = [a1, b1];
41+
// CHECK-NEXT: }
42+
43+
// CHECK-LABEL: def records_B {
44+
// CHECK-NEXT: list<B> records = [b0, b1];
45+
// CHECK-NEXT: }
46+
47+
#ifdef ERROR1
48+
defvar error1 = !records<A>(123)
49+
// ERROR1: error: expected string type argument in !records operator
50+
#endif
51+
52+
#ifdef ERROR2
53+
defvar error2 = !records<1>("")
54+
// ERROR2: error: Unknown token when expecting a type
55+
#endif
56+
57+
#ifdef ERROR3
58+
defvar error3 = !records<A>("([)]")
59+
// ERROR3: error: invalid regex '([)]'
60+
#endif

0 commit comments

Comments
 (0)