Skip to content

Commit 69b0b7b

Browse files
committed
[TableGen] Add !defined operator to get defined records
The format is: `!defined<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.
1 parent 9f61716 commit 69b0b7b

File tree

7 files changed

+217
-10
lines changed

7 files changed

+217
-10
lines changed

llvm/docs/TableGen/ProgRef.rst

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -220,16 +220,16 @@ TableGen provides "bang operators" that have a wide variety of uses:
220220
.. productionlist::
221221
BangOperator: one of
222222
: !add !and !cast !con !dag
223-
: !div !empty !eq !exists !filter
224-
: !find !foldl !foreach !ge !getdagarg
225-
: !getdagname !getdagop !gt !head !if
226-
: !initialized !interleave !isa !le !listconcat
227-
: !listflatten !listremove !listsplat !logtwo !lt
228-
: !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
223+
: !defined !div !empty !eq !exists
224+
: !filter !find !foldl !foreach !ge
225+
: !getdagarg !getdagname !getdagop !gt !head
226+
: !if !initialized !interleave !isa !le
227+
: !listconcat !listflatten !listremove !listsplat !logtwo
228+
: !lt !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
233233

234234
The ``!cond`` operator has a slightly different
235235
syntax compared to other bang operators, so it is defined separately:
@@ -1722,6 +1722,11 @@ and non-0 as true.
17221722
Example: ``!dag(op, [a1, a2, ?], ["name1", "name2", "name3"])`` results in
17231723
``(op a1-value:$name1, a2-value:$name2, ?:$name3)``.
17241724

1725+
``!defined<``\ *type*\ ``>([``\ *regex*\ ``])``
1726+
This operator produces a list of records whose type is *type*. If *regex*
1727+
is provided, only records whose name matches the regular expression *regex*
1728+
will be included.
1729+
17251730
``!div(``\ *a*\ ``,`` *b*\ ``)``
17261731
This operator performs signed division of *a* by *b*, and produces the quotient.
17271732
Division by 0 produces an error. Division of INT64_MIN by -1 produces an error.

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

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

llvm/lib/TableGen/Record.cpp

Lines changed: 61 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<DefinedOpInit> TheDefinedOpInitPool;
8688
DenseMap<std::pair<const RecTy *, const Init *>, VarInit *> TheVarInitPool;
8789
DenseMap<std::pair<const TypedInit *, unsigned>, VarBitInit *>
8890
TheVarBitInitPool;
@@ -2199,6 +2201,65 @@ std::string ExistsOpInit::getAsString() const {
21992201
.str();
22002202
}
22012203

2204+
static void ProfileDefinedOpInit(FoldingSetNodeID &ID, const RecTy *Type,
2205+
const Init *Regex) {
2206+
ID.AddPointer(Type);
2207+
ID.AddPointer(Regex);
2208+
}
2209+
2210+
const DefinedOpInit *DefinedOpInit::get(const RecTy *Type, const Init *Regex) {
2211+
FoldingSetNodeID ID;
2212+
ProfileDefinedOpInit(ID, Type, Regex);
2213+
2214+
detail::RecordKeeperImpl &RK = Regex->getRecordKeeper().getImpl();
2215+
void *IP = nullptr;
2216+
if (const DefinedOpInit *I =
2217+
RK.TheDefinedOpInitPool.FindNodeOrInsertPos(ID, IP))
2218+
return I;
2219+
2220+
DefinedOpInit *I = new (RK.Allocator) DefinedOpInit(Type, Regex);
2221+
RK.TheDefinedOpInitPool.InsertNode(I, IP);
2222+
return I;
2223+
}
2224+
2225+
void DefinedOpInit::Profile(FoldingSetNodeID &ID) const {
2226+
ProfileDefinedOpInit(ID, Type, Regex);
2227+
}
2228+
2229+
const Init *DefinedOpInit::Fold() const {
2230+
const auto *RegexInit = dyn_cast<StringInit>(Regex);
2231+
if (!RegexInit)
2232+
return this;
2233+
2234+
StringRef RegexStr = RegexInit->getValue();
2235+
llvm::Regex Matcher(RegexStr);
2236+
if (!Matcher.isValid())
2237+
PrintFatalError(Twine("invalid regex '") + RegexStr + Twine("'"));
2238+
2239+
const RecordKeeper &RK = Type->getRecordKeeper();
2240+
SmallVector<Init *, 8> Selected;
2241+
for (auto &Def : RK.getAllDerivedDefinitionsIfDefined(Type->getAsString()))
2242+
if (Matcher.match(Def->getName()))
2243+
Selected.push_back(Def->getDefInit());
2244+
2245+
return ListInit::get(Selected, Type);
2246+
}
2247+
2248+
const Init *DefinedOpInit::resolveReferences(Resolver &R) const {
2249+
const Init *NewRegex = Regex->resolveReferences(R);
2250+
if (Regex != NewRegex)
2251+
return get(Type, NewRegex)->Fold();
2252+
return this;
2253+
}
2254+
2255+
const Init *DefinedOpInit::getBit(unsigned Bit) const {
2256+
return VarBitInit::get(this, Bit);
2257+
}
2258+
2259+
std::string DefinedOpInit::getAsString() const {
2260+
return "!defined<" + Type->getAsString() + ">(" + Regex->getAsString() + ")";
2261+
}
2262+
22022263
const RecTy *TypedInit::getFieldType(const StringInit *FieldName) const {
22032264
if (const auto *RecordType = dyn_cast<RecordRecTy>(getType())) {
22042265
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
@@ -619,6 +619,7 @@ tgtok::TokKind TGLexer::LexExclaim() {
619619
.Case("sra", tgtok::XSRA)
620620
.Case("srl", tgtok::XSRL)
621621
.Case("cast", tgtok::XCast)
622+
.Case("defined", tgtok::XDefined)
622623
.Case("empty", tgtok::XEmpty)
623624
.Case("subst", tgtok::XSubst)
624625
.Case("foldl", tgtok::XFoldl)

llvm/lib/TableGen/TGLexer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ enum TokKind {
134134
XHead,
135135
XTail,
136136
XSize,
137+
XDefined,
137138
XEmpty,
138139
XInitialized,
139140
XIf,

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

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

0 commit comments

Comments
 (0)