Skip to content

Commit 6344848

Browse files
committed
[TableGen] Add new operator !exists
We can cast a string to a record via !cast, but we have no mechanism to check if it is valid and TableGen will raise an error if failed to cast. Besides, we have no semantic null in TableGen (we have `?` but different backends handle uninitialized value differently), so operator like `dyn_cast<>` is hard to implement. In this patch, we add a new operator `!exists<T>(s)` to check whether a record with type `T` and name `s` exists. Self-references are allowed just like `!cast`. By doing these, we can write code like: ``` class dyn_cast_to_record<string name> { R value = !if(!exists<R>(name), !cast<R>(name), default_value); } defvar v = dyn_cast_to_record<"R0">.value; // R0 or default_value. ``` Reviewed By: tra, nhaehnle Differential Revision: https://reviews.llvm.org/D127948
1 parent 1cb8c87 commit 6344848

File tree

10 files changed

+255
-0
lines changed

10 files changed

+255
-0
lines changed

llvm/docs/TableGen/ProgRef.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,6 +1724,10 @@ and non-0 as true.
17241724
This operator produces 1 if the type of *a* is a subtype of the given *type*; 0
17251725
otherwise.
17261726

1727+
``!exists<``\ *type*\ ``>(``\ *name*\ ``)``
1728+
This operator produces 1 if a record of the given *type* whose name is *name*
1729+
exists; 0 otherwise. *name* should be of type *string*.
1730+
17271731
``!le(``\ *a*\ ``,`` *b*\ ``)``
17281732
This operator produces 1 if *a* is less than or equal to *b*; 0 otherwise.
17291733
The arguments must be ``bit``, ``bits``, ``int``, or ``string`` values.

llvm/include/llvm/TableGen/Record.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ class Init {
311311
IK_CondOpInit,
312312
IK_FoldOpInit,
313313
IK_IsAOpInit,
314+
IK_ExistsOpInit,
314315
IK_AnonymousNameInit,
315316
IK_StringInit,
316317
IK_VarInit,
@@ -1095,6 +1096,40 @@ class IsAOpInit : public TypedInit, public FoldingSetNode {
10951096
std::string getAsString() const override;
10961097
};
10971098

1099+
/// !exists<type>(expr) - Dynamically determine if a record of `type` named
1100+
/// `expr` exists.
1101+
class ExistsOpInit : public TypedInit, public FoldingSetNode {
1102+
private:
1103+
RecTy *CheckType;
1104+
Init *Expr;
1105+
1106+
ExistsOpInit(RecTy *CheckType, Init *Expr)
1107+
: TypedInit(IK_ExistsOpInit, IntRecTy::get(CheckType->getRecordKeeper())),
1108+
CheckType(CheckType), Expr(Expr) {}
1109+
1110+
public:
1111+
ExistsOpInit(const ExistsOpInit &) = delete;
1112+
ExistsOpInit &operator=(const ExistsOpInit &) = delete;
1113+
1114+
static bool classof(const Init *I) { return I->getKind() == IK_ExistsOpInit; }
1115+
1116+
static ExistsOpInit *get(RecTy *CheckType, Init *Expr);
1117+
1118+
void Profile(FoldingSetNodeID &ID) const;
1119+
1120+
// Fold - If possible, fold this to a simpler init. Return this if not
1121+
// possible to fold.
1122+
Init *Fold(Record *CurRec, bool IsFinal = false) const;
1123+
1124+
bool isComplete() const override { return false; }
1125+
1126+
Init *resolveReferences(Resolver &R) const override;
1127+
1128+
Init *getBit(unsigned Bit) const override;
1129+
1130+
std::string getAsString() const override;
1131+
};
1132+
10981133
/// 'Opcode' - Represent a reference to an entire variable object.
10991134
class VarInit : public TypedInit {
11001135
Init *VarName;

llvm/lib/TableGen/Record.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ struct RecordKeeperImpl {
7979
FoldingSet<TernOpInit> TheTernOpInitPool;
8080
FoldingSet<FoldOpInit> TheFoldOpInitPool;
8181
FoldingSet<IsAOpInit> TheIsAOpInitPool;
82+
FoldingSet<ExistsOpInit> TheExistsOpInitPool;
8283
DenseMap<std::pair<RecTy *, Init *>, VarInit *> TheVarInitPool;
8384
DenseMap<std::pair<TypedInit *, unsigned>, VarBitInit *> TheVarBitInitPool;
8485
DenseMap<std::pair<TypedInit *, unsigned>, VarListElementInit *>
@@ -1660,6 +1661,81 @@ std::string IsAOpInit::getAsString() const {
16601661
.str();
16611662
}
16621663

1664+
static void ProfileExistsOpInit(FoldingSetNodeID &ID, RecTy *CheckType,
1665+
Init *Expr) {
1666+
ID.AddPointer(CheckType);
1667+
ID.AddPointer(Expr);
1668+
}
1669+
1670+
ExistsOpInit *ExistsOpInit::get(RecTy *CheckType, Init *Expr) {
1671+
FoldingSetNodeID ID;
1672+
ProfileExistsOpInit(ID, CheckType, Expr);
1673+
1674+
detail::RecordKeeperImpl &RK = Expr->getRecordKeeper().getImpl();
1675+
void *IP = nullptr;
1676+
if (ExistsOpInit *I = RK.TheExistsOpInitPool.FindNodeOrInsertPos(ID, IP))
1677+
return I;
1678+
1679+
ExistsOpInit *I = new (RK.Allocator) ExistsOpInit(CheckType, Expr);
1680+
RK.TheExistsOpInitPool.InsertNode(I, IP);
1681+
return I;
1682+
}
1683+
1684+
void ExistsOpInit::Profile(FoldingSetNodeID &ID) const {
1685+
ProfileExistsOpInit(ID, CheckType, Expr);
1686+
}
1687+
1688+
Init *ExistsOpInit::Fold(Record *CurRec, bool IsFinal) const {
1689+
if (StringInit *Name = dyn_cast<StringInit>(Expr)) {
1690+
if (!CurRec && !IsFinal)
1691+
return const_cast<ExistsOpInit *>(this);
1692+
1693+
// Self-references are allowed, but their resolution is delayed until
1694+
// the final resolve to ensure that we get the correct type for them.
1695+
auto *Anonymous = dyn_cast<AnonymousNameInit>(CurRec->getNameInit());
1696+
if (Name == CurRec->getNameInit() ||
1697+
(Anonymous && Name == Anonymous->getNameInit())) {
1698+
if (!IsFinal)
1699+
return const_cast<ExistsOpInit *>(this);
1700+
1701+
// No doubt that there exists a record, so we should check if types are
1702+
// compatiable.
1703+
return IntInit::get(getRecordKeeper(),
1704+
CurRec->getType()->typeIsA(CheckType));
1705+
}
1706+
1707+
// Look up all defined records to see if we can find one.
1708+
Record *D = CheckType->getRecordKeeper().getDef(Name->getValue());
1709+
if (!D) {
1710+
if (IsFinal)
1711+
return IntInit::get(getRecordKeeper(), 0);
1712+
return const_cast<ExistsOpInit *>(this);
1713+
}
1714+
1715+
// Check if types are compatiable.
1716+
return IntInit::get(getRecordKeeper(),
1717+
DefInit::get(D)->getType()->typeIsA(CheckType));
1718+
}
1719+
return const_cast<ExistsOpInit *>(this);
1720+
}
1721+
1722+
Init *ExistsOpInit::resolveReferences(Resolver &R) const {
1723+
Init *NewExpr = Expr->resolveReferences(R);
1724+
if (Expr != NewExpr || R.isFinal())
1725+
return get(CheckType, NewExpr)->Fold(R.getCurrentRecord(), R.isFinal());
1726+
return const_cast<ExistsOpInit *>(this);
1727+
}
1728+
1729+
Init *ExistsOpInit::getBit(unsigned Bit) const {
1730+
return VarBitInit::get(const_cast<ExistsOpInit *>(this), Bit);
1731+
}
1732+
1733+
std::string ExistsOpInit::getAsString() const {
1734+
return (Twine("!exists<") + CheckType->getAsString() + ">(" +
1735+
Expr->getAsString() + ")")
1736+
.str();
1737+
}
1738+
16631739
RecTy *TypedInit::getFieldType(StringInit *FieldName) const {
16641740
if (RecordRecTy *RecordType = dyn_cast<RecordRecTy>(getType())) {
16651741
for (Record *Rec : RecordType->getClasses()) {

llvm/lib/TableGen/TGLexer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,7 @@ tgtok::TokKind TGLexer::LexExclaim() {
584584
.Case("find", tgtok::XFind)
585585
.Cases("setdagop", "setop", tgtok::XSetDagOp) // !setop is deprecated.
586586
.Cases("getdagop", "getop", tgtok::XGetDagOp) // !getop is deprecated.
587+
.Case("exists", tgtok::XExists)
587588
.Default(tgtok::Error);
588589

589590
return Kind != tgtok::Error ? Kind : ReturnError(Start-1, "Unknown operator");

llvm/lib/TableGen/TGLexer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ namespace tgtok {
5656
XListConcat, XListSplat, XStrConcat, XInterleave, XSubstr, XFind, XCast,
5757
XSubst, XForEach, XFilter, XFoldl, XHead, XTail, XSize, XEmpty, XIf,
5858
XCond, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt, XSetDagOp, XGetDagOp,
59+
XExists,
5960

6061
// Boolean literals.
6162
TrueVal, FalseVal,

llvm/lib/TableGen/TGParser.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,52 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
10961096
return (IsAOpInit::get(Type, LHS))->Fold();
10971097
}
10981098

1099+
case tgtok::XExists: {
1100+
// Value ::= !exists '<' Type '>' '(' Value ')'
1101+
Lex.Lex(); // eat the operation
1102+
1103+
RecTy *Type = ParseOperatorType();
1104+
if (!Type)
1105+
return nullptr;
1106+
1107+
if (!consume(tgtok::l_paren)) {
1108+
TokError("expected '(' after type of !exists");
1109+
return nullptr;
1110+
}
1111+
1112+
SMLoc ExprLoc = Lex.getLoc();
1113+
Init *Expr = ParseValue(CurRec);
1114+
if (!Expr)
1115+
return nullptr;
1116+
1117+
TypedInit *ExprType = dyn_cast<TypedInit>(Expr);
1118+
if (!ExprType) {
1119+
Error(ExprLoc, "expected string type argument in !exists operator");
1120+
return nullptr;
1121+
}
1122+
1123+
RecordRecTy *RecType = dyn_cast<RecordRecTy>(ExprType->getType());
1124+
if (RecType) {
1125+
Error(ExprLoc,
1126+
"expected string type argument in !exists operator, please "
1127+
"use !isa instead");
1128+
return nullptr;
1129+
}
1130+
1131+
StringRecTy *SType = dyn_cast<StringRecTy>(ExprType->getType());
1132+
if (!SType) {
1133+
Error(ExprLoc, "expected string type argument in !exists operator");
1134+
return nullptr;
1135+
}
1136+
1137+
if (!consume(tgtok::r_paren)) {
1138+
TokError("expected ')' in !exists");
1139+
return nullptr;
1140+
}
1141+
1142+
return (ExistsOpInit::get(Type, Expr))->Fold(CurRec);
1143+
}
1144+
10991145
case tgtok::XConcat:
11001146
case tgtok::XADD:
11011147
case tgtok::XSUB:
@@ -2358,6 +2404,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
23582404
case tgtok::XEmpty:
23592405
case tgtok::XCast:
23602406
case tgtok::XGetDagOp: // Value ::= !unop '(' Value ')'
2407+
case tgtok::XExists:
23612408
case tgtok::XIsA:
23622409
case tgtok::XConcat:
23632410
case tgtok::XDag:
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: not llvm-tblgen --no-warn-on-unused-template-args %s 2>&1 | FileCheck %s
2+
// XFAIL: vg_leak
3+
4+
//CHECK: expected string type argument in !exists operator
5+
6+
class A;
7+
8+
defvar value = !exists<A>(123);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: not llvm-tblgen --no-warn-on-unused-template-args %s 2>&1 | FileCheck %s
2+
// XFAIL: vg_leak
3+
4+
//CHECK: expected string type argument in !exists operator, please use !isa instead
5+
6+
class A;
7+
def a : A;
8+
defvar value = !exists<A>(a);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: not llvm-tblgen --no-warn-on-unused-template-args %s 2>&1 | FileCheck %s
2+
// XFAIL: vg_leak
3+
4+
//CHECK: expected string type argument in !exists operator
5+
6+
class A;
7+
8+
defvar value = !exists<A>(?);

llvm/test/TableGen/exists.td

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// RUN: llvm-tblgen --no-warn-on-unused-template-args %s | FileCheck %s
2+
// XFAIL: vg_leak
3+
4+
class A;
5+
def a0 : A;
6+
7+
class A_check<string name>{
8+
int exists = !exists<A>(name);
9+
}
10+
11+
def a0_exists : A_check<"a0">;
12+
def a1_missing : A_check<"a1">;
13+
14+
15+
// Subclasses are allowed.
16+
17+
class B;
18+
class SubOfB : B;
19+
class B_check<string name> {
20+
int exists = !exists<B>(name);
21+
}
22+
23+
def sub : SubOfB;
24+
25+
def sub_exists : B_check<"sub">;
26+
def a0_is_not_sub_of_B : B_check<"a0">;
27+
28+
29+
// Self-references are allowed.
30+
31+
class Self_check<string name> {
32+
int exists = !exists<Self_check>(name);
33+
}
34+
35+
def self_reference : Self_check<"self_reference">; // Self-reference
36+
// There is no record called `current` in current context though we will define it below.
37+
def current_missing : Self_check<"current">;
38+
def current : Self_check<"current">;
39+
40+
// CHECK: def a0_exists {
41+
// CHECK: int exists = 1;
42+
// CHECK: }
43+
44+
// CHECK: def a0_is_not_sub_of_B {
45+
// CHECK: int exists = 0;
46+
// CHECK: }
47+
48+
// CHECK: def a1_missing {
49+
// CHECK: int exists = 0;
50+
// CHECK: }
51+
52+
// CHECK: def current {
53+
// CHECK: int exists = 1;
54+
// CHECK: }
55+
56+
// `current` doesn't exist because we define it below `current_missing`.
57+
// CHECK: def current_missing {
58+
// CHECK: int exists = 0;
59+
// CHECK: }
60+
61+
// CHECK: def self_reference {
62+
// CHECK: int exists = 1;
63+
// CHECK: }
64+
65+
// CHECK: def sub_exists {
66+
// CHECK: int exists = 1;
67+
// CHECK: }

0 commit comments

Comments
 (0)