diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst --- a/llvm/docs/TableGen/ProgRef.rst +++ b/llvm/docs/TableGen/ProgRef.rst @@ -1724,6 +1724,10 @@ This operator produces 1 if the type of *a* is a subtype of the given *type*; 0 otherwise. +``!exists<``\ *type*\ ``>(``\ *name*\ ``)`` + This operator produces 1 if a record of the given *type* whose name is *name* + exists; 0 otherwise. *name* should be of type *string*. + ``!le(``\ *a*\ ``,`` *b*\ ``)`` This operator produces 1 if *a* is less than or equal to *b*; 0 otherwise. The arguments must be ``bit``, ``bits``, ``int``, or ``string`` values. diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h --- a/llvm/include/llvm/TableGen/Record.h +++ b/llvm/include/llvm/TableGen/Record.h @@ -311,6 +311,7 @@ IK_CondOpInit, IK_FoldOpInit, IK_IsAOpInit, + IK_ExistsOpInit, IK_AnonymousNameInit, IK_StringInit, IK_VarInit, @@ -1095,6 +1096,40 @@ std::string getAsString() const override; }; +/// !exists(expr) - Dynamically determine if a record of `type` named +/// `expr` exists. +class ExistsOpInit : public TypedInit, public FoldingSetNode { +private: + RecTy *CheckType; + Init *Expr; + + ExistsOpInit(RecTy *CheckType, Init *Expr) + : TypedInit(IK_ExistsOpInit, IntRecTy::get(CheckType->getRecordKeeper())), + CheckType(CheckType), Expr(Expr) {} + +public: + ExistsOpInit(const ExistsOpInit &) = delete; + ExistsOpInit &operator=(const ExistsOpInit &) = delete; + + static bool classof(const Init *I) { return I->getKind() == IK_ExistsOpInit; } + + static ExistsOpInit *get(RecTy *CheckType, Init *Expr); + + void Profile(FoldingSetNodeID &ID) const; + + // Fold - If possible, fold this to a simpler init. Return this if not + // possible to fold. + Init *Fold(Record *CurRec, bool IsFinal = false) const; + + bool isComplete() const override { return false; } + + Init *resolveReferences(Resolver &R) const override; + + Init *getBit(unsigned Bit) const override; + + std::string getAsString() const override; +}; + /// 'Opcode' - Represent a reference to an entire variable object. class VarInit : public TypedInit { Init *VarName; diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -79,6 +79,7 @@ FoldingSet TheTernOpInitPool; FoldingSet TheFoldOpInitPool; FoldingSet TheIsAOpInitPool; + FoldingSet TheExistsOpInitPool; DenseMap, VarInit *> TheVarInitPool; DenseMap, VarBitInit *> TheVarBitInitPool; DenseMap, VarListElementInit *> @@ -1660,6 +1661,81 @@ .str(); } +static void ProfileExistsOpInit(FoldingSetNodeID &ID, RecTy *CheckType, + Init *Expr) { + ID.AddPointer(CheckType); + ID.AddPointer(Expr); +} + +ExistsOpInit *ExistsOpInit::get(RecTy *CheckType, Init *Expr) { + FoldingSetNodeID ID; + ProfileExistsOpInit(ID, CheckType, Expr); + + detail::RecordKeeperImpl &RK = Expr->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (ExistsOpInit *I = RK.TheExistsOpInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + ExistsOpInit *I = new (RK.Allocator) ExistsOpInit(CheckType, Expr); + RK.TheExistsOpInitPool.InsertNode(I, IP); + return I; +} + +void ExistsOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileExistsOpInit(ID, CheckType, Expr); +} + +Init *ExistsOpInit::Fold(Record *CurRec, bool IsFinal) const { + if (StringInit *Name = dyn_cast(Expr)) { + if (!CurRec && !IsFinal) + return const_cast(this); + + // Self-references are allowed, but their resolution is delayed until + // the final resolve to ensure that we get the correct type for them. + auto *Anonymous = dyn_cast(CurRec->getNameInit()); + if (Name == CurRec->getNameInit() || + (Anonymous && Name == Anonymous->getNameInit())) { + if (!IsFinal) + return const_cast(this); + + // No doubt that there exists a record, so we should check if types are + // compatiable. + return IntInit::get(getRecordKeeper(), + CurRec->getType()->typeIsA(CheckType)); + } + + // Look up all defined records to see if we can find one. + Record *D = CheckType->getRecordKeeper().getDef(Name->getValue()); + if (!D) { + if (IsFinal) + return IntInit::get(getRecordKeeper(), 0); + return const_cast(this); + } + + // Check if types are compatiable. + return IntInit::get(getRecordKeeper(), + DefInit::get(D)->getType()->typeIsA(CheckType)); + } + return const_cast(this); +} + +Init *ExistsOpInit::resolveReferences(Resolver &R) const { + Init *NewExpr = Expr->resolveReferences(R); + if (Expr != NewExpr || R.isFinal()) + return get(CheckType, NewExpr)->Fold(R.getCurrentRecord(), R.isFinal()); + return const_cast(this); +} + +Init *ExistsOpInit::getBit(unsigned Bit) const { + return VarBitInit::get(const_cast(this), Bit); +} + +std::string ExistsOpInit::getAsString() const { + return (Twine("!exists<") + CheckType->getAsString() + ">(" + + Expr->getAsString() + ")") + .str(); +} + RecTy *TypedInit::getFieldType(StringInit *FieldName) const { if (RecordRecTy *RecordType = dyn_cast(getType())) { for (Record *Rec : RecordType->getClasses()) { diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -56,6 +56,7 @@ XListConcat, XListSplat, XStrConcat, XInterleave, XSubstr, XFind, XCast, XSubst, XForEach, XFilter, XFoldl, XHead, XTail, XSize, XEmpty, XIf, XCond, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt, XSetDagOp, XGetDagOp, + XExists, // Boolean literals. TrueVal, FalseVal, diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -584,6 +584,7 @@ .Case("find", tgtok::XFind) .Cases("setdagop", "setop", tgtok::XSetDagOp) // !setop is deprecated. .Cases("getdagop", "getop", tgtok::XGetDagOp) // !getop is deprecated. + .Case("exists", tgtok::XExists) .Default(tgtok::Error); return Kind != tgtok::Error ? Kind : ReturnError(Start-1, "Unknown operator"); diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -1096,6 +1096,52 @@ return (IsAOpInit::get(Type, LHS))->Fold(); } + case tgtok::XExists: { + // Value ::= !exists '<' Type '>' '(' Value ')' + Lex.Lex(); // eat the operation + + RecTy *Type = ParseOperatorType(); + if (!Type) + return nullptr; + + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after type of !exists"); + return nullptr; + } + + SMLoc ExprLoc = Lex.getLoc(); + Init *Expr = ParseValue(CurRec); + if (!Expr) + return nullptr; + + TypedInit *ExprType = dyn_cast(Expr); + if (!ExprType) { + Error(ExprLoc, "expected string type argument in !exists operator"); + return nullptr; + } + + RecordRecTy *RecType = dyn_cast(ExprType->getType()); + if (RecType) { + Error(ExprLoc, + "expected string type argument in !exists operator, please " + "use !isa instead"); + return nullptr; + } + + StringRecTy *SType = dyn_cast(ExprType->getType()); + if (!SType) { + Error(ExprLoc, "expected string type argument in !exists operator"); + return nullptr; + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in !exists"); + return nullptr; + } + + return (ExistsOpInit::get(Type, Expr))->Fold(CurRec); + } + case tgtok::XConcat: case tgtok::XADD: case tgtok::XSUB: @@ -2358,6 +2404,7 @@ case tgtok::XEmpty: case tgtok::XCast: case tgtok::XGetDagOp: // Value ::= !unop '(' Value ')' + case tgtok::XExists: case tgtok::XIsA: case tgtok::XConcat: case tgtok::XDag: diff --git a/llvm/test/TableGen/exists-error-non-string.td b/llvm/test/TableGen/exists-error-non-string.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/exists-error-non-string.td @@ -0,0 +1,8 @@ +// RUN: not llvm-tblgen --no-warn-on-unused-template-args %s 2>&1 | FileCheck %s +// XFAIL: vg_leak + +//CHECK: expected string type argument in !exists operator + +class A; + +defvar value = !exists(123); diff --git a/llvm/test/TableGen/exists-error-record.td b/llvm/test/TableGen/exists-error-record.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/exists-error-record.td @@ -0,0 +1,8 @@ +// RUN: not llvm-tblgen --no-warn-on-unused-template-args %s 2>&1 | FileCheck %s +// XFAIL: vg_leak + +//CHECK: expected string type argument in !exists operator, please use !isa instead + +class A; +def a : A; +defvar value = !exists(a); diff --git a/llvm/test/TableGen/exists-error-uninitialized.td b/llvm/test/TableGen/exists-error-uninitialized.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/exists-error-uninitialized.td @@ -0,0 +1,8 @@ +// RUN: not llvm-tblgen --no-warn-on-unused-template-args %s 2>&1 | FileCheck %s +// XFAIL: vg_leak + +//CHECK: expected string type argument in !exists operator + +class A; + +defvar value = !exists(?); diff --git a/llvm/test/TableGen/exists.td b/llvm/test/TableGen/exists.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/exists.td @@ -0,0 +1,67 @@ +// RUN: llvm-tblgen --no-warn-on-unused-template-args %s | FileCheck %s +// XFAIL: vg_leak + +class A; +def a0 : A; + +class A_check{ + int exists = !exists(name); +} + +def a0_exists : A_check<"a0">; +def a1_missing : A_check<"a1">; + + +// Subclasses are allowed. + +class B; +class SubOfB : B; +class B_check { + int exists = !exists(name); +} + +def sub : SubOfB; + +def sub_exists : B_check<"sub">; +def a0_is_not_sub_of_B : B_check<"a0">; + + +// Self-references are allowed. + +class Self_check { + int exists = !exists(name); +} + +def self_reference : Self_check<"self_reference">; // Self-reference +// There is no record called `current` in current context though we will define it below. +def current_missing : Self_check<"current">; +def current : Self_check<"current">; + +// CHECK: def a0_exists { +// CHECK: int exists = 1; +// CHECK: } + +// CHECK: def a0_is_not_sub_of_B { +// CHECK: int exists = 0; +// CHECK: } + +// CHECK: def a1_missing { +// CHECK: int exists = 0; +// CHECK: } + +// CHECK: def current { +// CHECK: int exists = 1; +// CHECK: } + +// `current` doesn't exist because we define it below `current_missing`. +// CHECK: def current_missing { +// CHECK: int exists = 0; +// CHECK: } + +// CHECK: def self_reference { +// CHECK: int exists = 1; +// CHECK: } + +// CHECK: def sub_exists { +// CHECK: int exists = 1; +// CHECK: }