Skip to content

Commit 8836128

Browse files
authored
[TableGen] Add !instances operator to get defined records (#129680)
The format is: `!instances<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 b52977b commit 8836128

File tree

7 files changed

+298
-7
lines changed

7 files changed

+298
-7
lines changed

llvm/docs/TableGen/ProgRef.rst

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,13 +223,13 @@ TableGen provides "bang operators" that have a wide variety of uses:
223223
: !div !empty !eq !exists !filter
224224
: !find !foldl !foreach !ge !getdagarg
225225
: !getdagname !getdagop !gt !head !if
226-
: !initialized !interleave !isa !le !listconcat
227-
: !listflatten !listremove !listsplat !logtwo !lt
228-
: !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
226+
: !initialized !instances !interleave !isa !le
227+
: !listconcat !listflatten !listremove !listsplat !logtwo
228+
: !lt !match !mul !ne !not
229+
: !or !range !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:
@@ -1836,6 +1836,15 @@ and non-0 as true.
18361836
This operator produces 1 if *a* is not the uninitialized value (``?``) and 0
18371837
otherwise.
18381838

1839+
``!instances<``\ *type*\ ``>([``\ *regex*\ ``])``
1840+
This operator produces a list of records whose type is *type*. If *regex*
1841+
is provided, only records whose name matches the regular expression *regex*
1842+
will be included. The format of *regex* is ERE (Extended POSIX Regular
1843+
Expressions).
1844+
1845+
If ``!instances`` is in a class/multiclass/foreach, only these records of
1846+
*type* that have been instantiated will be considered.
1847+
18391848
``!interleave(``\ *list*\ ``,`` *delim*\ ``)``
18401849
This operator concatenates the items in the *list*, interleaving the
18411850
*delim* string between each pair, and produces the resulting string.

llvm/include/llvm/TableGen/Record.h

Lines changed: 39 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_InstancesOpInit,
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+
/// !instances<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 InstancesOpInit final : public TypedInit, public FoldingSetNode {
1200+
private:
1201+
const RecTy *Type;
1202+
const Init *Regex;
1203+
1204+
InstancesOpInit(const RecTy *Type, const Init *Regex)
1205+
: TypedInit(IK_InstancesOpInit, ListRecTy::get(Type)), Type(Type),
1206+
Regex(Regex) {}
1207+
1208+
public:
1209+
InstancesOpInit(const InstancesOpInit &) = delete;
1210+
InstancesOpInit &operator=(const InstancesOpInit &) = delete;
1211+
1212+
static bool classof(const Init *I) {
1213+
return I->getKind() == IK_InstancesOpInit;
1214+
}
1215+
1216+
static const InstancesOpInit *get(const RecTy *Type, const Init *Regex);
1217+
1218+
void Profile(FoldingSetNodeID &ID) const;
1219+
1220+
const Init *Fold(const Record *CurRec, bool IsFinal = false) 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;
@@ -1982,6 +2018,9 @@ class RecordKeeper {
19822018
bool Ins = Defs.try_emplace(std::string(R->getName()), std::move(R)).second;
19832019
(void)Ins;
19842020
assert(Ins && "Record already exists");
2021+
// Clear cache
2022+
if (!Cache.empty())
2023+
Cache.clear();
19852024
}
19862025

19872026
void addExtraGlobal(StringRef Name, const Init *I) {

llvm/lib/TableGen/Record.cpp

Lines changed: 65 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<InstancesOpInit> TheInstancesOpInitPool;
8788
DenseMap<std::pair<const RecTy *, const Init *>, VarInit *> TheVarInitPool;
8889
DenseMap<std::pair<const TypedInit *, unsigned>, VarBitInit *>
8990
TheVarBitInitPool;
@@ -2222,6 +2223,70 @@ std::string ExistsOpInit::getAsString() const {
22222223
.str();
22232224
}
22242225

2226+
static void ProfileInstancesOpInit(FoldingSetNodeID &ID, const RecTy *Type,
2227+
const Init *Regex) {
2228+
ID.AddPointer(Type);
2229+
ID.AddPointer(Regex);
2230+
}
2231+
2232+
const InstancesOpInit *InstancesOpInit::get(const RecTy *Type,
2233+
const Init *Regex) {
2234+
FoldingSetNodeID ID;
2235+
ProfileInstancesOpInit(ID, Type, Regex);
2236+
2237+
detail::RecordKeeperImpl &RK = Regex->getRecordKeeper().getImpl();
2238+
void *IP = nullptr;
2239+
if (const InstancesOpInit *I =
2240+
RK.TheInstancesOpInitPool.FindNodeOrInsertPos(ID, IP))
2241+
return I;
2242+
2243+
InstancesOpInit *I = new (RK.Allocator) InstancesOpInit(Type, Regex);
2244+
RK.TheInstancesOpInitPool.InsertNode(I, IP);
2245+
return I;
2246+
}
2247+
2248+
void InstancesOpInit::Profile(FoldingSetNodeID &ID) const {
2249+
ProfileInstancesOpInit(ID, Type, Regex);
2250+
}
2251+
2252+
const Init *InstancesOpInit::Fold(const Record *CurRec, bool IsFinal) const {
2253+
if (CurRec && !IsFinal)
2254+
return this;
2255+
2256+
const auto *RegexInit = dyn_cast<StringInit>(Regex);
2257+
if (!RegexInit)
2258+
return this;
2259+
2260+
StringRef RegexStr = RegexInit->getValue();
2261+
llvm::Regex Matcher(RegexStr);
2262+
if (!Matcher.isValid())
2263+
PrintFatalError(Twine("invalid regex '") + RegexStr + Twine("'"));
2264+
2265+
const RecordKeeper &RK = Type->getRecordKeeper();
2266+
SmallVector<Init *, 8> Selected;
2267+
for (auto &Def : RK.getAllDerivedDefinitionsIfDefined(Type->getAsString()))
2268+
if (Matcher.match(Def->getName()))
2269+
Selected.push_back(Def->getDefInit());
2270+
2271+
return ListInit::get(Selected, Type);
2272+
}
2273+
2274+
const Init *InstancesOpInit::resolveReferences(Resolver &R) const {
2275+
const Init *NewRegex = Regex->resolveReferences(R);
2276+
if (Regex != NewRegex || R.isFinal())
2277+
return get(Type, NewRegex)->Fold(R.getCurrentRecord(), R.isFinal());
2278+
return this;
2279+
}
2280+
2281+
const Init *InstancesOpInit::getBit(unsigned Bit) const {
2282+
return VarBitInit::get(this, Bit);
2283+
}
2284+
2285+
std::string InstancesOpInit::getAsString() const {
2286+
return "!instances<" + Type->getAsString() + ">(" + Regex->getAsString() +
2287+
")";
2288+
}
2289+
22252290
const RecTy *TypedInit::getFieldType(const StringInit *FieldName) const {
22262291
if (const auto *RecordType = dyn_cast<RecordRecTy>(getType())) {
22272292
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
@@ -632,6 +632,7 @@ tgtok::TokKind TGLexer::LexExclaim() {
632632
.Case("strconcat", tgtok::XStrConcat)
633633
.Case("initialized", tgtok::XInitialized)
634634
.Case("interleave", tgtok::XInterleave)
635+
.Case("instances", tgtok::XInstances)
635636
.Case("substr", tgtok::XSubstr)
636637
.Case("find", tgtok::XFind)
637638
.Cases("setdagop", "setop", tgtok::XSetDagOp) // !setop is deprecated.

llvm/lib/TableGen/TGLexer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ enum TokKind {
137137
XSize,
138138
XEmpty,
139139
XInitialized,
140+
XInstances,
140141
XIf,
141142
XCond,
142143
XEq,

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::XInstances: {
1459+
// Value ::= !instances '<' 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 !instances");
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 !instances 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 !instances 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 !instances");
1495+
return nullptr;
1496+
}
1497+
1498+
return InstancesOpInit::get(Type, Regex)->Fold(CurRec);
1499+
}
1500+
14581501
case tgtok::XConcat:
14591502
case tgtok::XMatch:
14601503
case tgtok::XADD:

llvm/test/TableGen/instances.td

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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+
// CHECK-LABEL: def test0_instances_A {
16+
// CHECK-NEXT: list<A> instances = [a0, a1, b0, b1];
17+
// CHECK-NEXT: }
18+
def test0_instances_A {
19+
list<A> instances = !instances<A>();
20+
}
21+
22+
// CHECK-LABEL: def test1_instances_A_x0 {
23+
// CHECK-NEXT: list<A> instances = [a0, b0];
24+
// CHECK-NEXT: }
25+
def test1_instances_A_x0 {
26+
list<A> instances = !instances<A>(".*0");
27+
}
28+
29+
// CHECK-LABEL: def test2_instances_A_x1 {
30+
// CHECK-NEXT: list<A> instances = [a1, b1];
31+
// CHECK-NEXT: }
32+
def test2_instances_A_x1 {
33+
list<A> instances = !instances<A>(".*1");
34+
}
35+
36+
// CHECK-LABEL: def test3_instances_B {
37+
// CHECK-NEXT: list<B> instances = [b0, b1];
38+
// CHECK-NEXT: }
39+
def test3_instances_B {
40+
list<B> instances = !instances<B>();
41+
}
42+
43+
//-----------------------------------------------------------------------------//
44+
45+
def a2 : A;
46+
def b2 : B;
47+
48+
class ClassTest {
49+
list<A> instances_A = !instances<A>();
50+
list<B> instances_B = !instances<B>();
51+
}
52+
53+
def a3 : A;
54+
def b3 : B;
55+
56+
def test4_in_class_def : ClassTest;
57+
// CHECK-LABEL: def test4_in_class_def {
58+
// CHECK-NEXT: list<A> instances_A = [a0, a1, a2, a3, b0, b1, b2, b3];
59+
// CHECK-NEXT: list<B> instances_B = [b0, b1, b2, b3];
60+
// CHECK-NEXT: }
61+
62+
//-----------------------------------------------------------------------------//
63+
// Self-recurrence is not supported, so it won't be count in.
64+
65+
// CHECK-LABEL: def test5_self_recurrence {
66+
// CHECK-NEXT: list<A> instances_A = [a0, a1, a2, a3, b0, b1, b2, b3];
67+
// CHECK-NEXT: }
68+
def test5_self_recurrence : A {
69+
list<A> instances_A = !instances<A>();
70+
}
71+
72+
//-----------------------------------------------------------------------------//
73+
// Test these in multiclasses/loops.
74+
75+
class C {
76+
list<C> instances_C = !instances<C>();
77+
}
78+
79+
multiclass MultiClassTest {
80+
foreach i = 0-2 in {
81+
def "c"#i : C;
82+
}
83+
}
84+
85+
// CHECK-LABEL: def test6_in_multiclass_def_c0 {
86+
// CHECK-NEXT: list<C> instances_C = [];
87+
// CHECK-NEXT: }
88+
// CHECK-LABEL: def test6_in_multiclass_def_c1 {
89+
// CHECK-NEXT: list<C> instances_C = [test6_in_multiclass_def_c0];
90+
// CHECK-NEXT: }
91+
// CHECK-LABEL: def test6_in_multiclass_def_c2 {
92+
// CHECK-NEXT: list<C> instances_C = [test6_in_multiclass_def_c0, test6_in_multiclass_def_c1];
93+
// CHECK-NEXT: }
94+
defm test6_in_multiclass_def_ : MultiClassTest;
95+
96+
//-----------------------------------------------------------------------------//
97+
// Default argument/temporary actual parameter will be considered as well.
98+
class D<int n>;
99+
100+
class TestArgument<D d = D<0>> {
101+
list<D> instances_D = !instances<D>();
102+
}
103+
104+
// CHECK-LABEL: def test7_default_arg {
105+
// CHECK-NEXT: list<D> instances_D = [anonymous_0];
106+
// CHECK-NEXT: }
107+
def test7_default_arg : TestArgument;
108+
109+
// CHECK-LABEL: def test8_anonymous0_arg {
110+
// CHECK-NEXT: list<D> instances_D = [anonymous_0, anonymous_1];
111+
// CHECK-NEXT: }
112+
// CHECK-LABEL: def test8_anonymous1_arg {
113+
// CHECK-NEXT: list<D> instances_D = [anonymous_0, anonymous_1, anonymous_2];
114+
// CHECK-NEXT: }
115+
def test8_anonymous0_arg : TestArgument<D<1>>;
116+
def test8_anonymous1_arg : TestArgument<D<2>>;
117+
118+
//-----------------------------------------------------------------------------//
119+
120+
#ifdef ERROR1
121+
defvar error1 = !instances<A>(123);
122+
// ERROR1: error: expected string type argument in !instances operator
123+
#endif
124+
125+
#ifdef ERROR2
126+
defvar error2 = !instances<1>("");
127+
// ERROR2: error: Unknown token when expecting a type
128+
#endif
129+
130+
#ifdef ERROR3
131+
defvar error3 = !instances<A>("([)]");
132+
// ERROR3: error: invalid regex '([)]'
133+
#endif

0 commit comments

Comments
 (0)